sqflite数据库 - Flutter在鸿蒙平台存储关系型数据


概述
在Flutter应用开发中,当需要存储大量关系型数据时,Hive和SharedPreferences已经不能满足需求。sqflite是Flutter平台上的SQLite数据库插件,提供了完整的SQL关系型数据库功能,支持复杂查询、事务操作、索引优化等高级特性。
本文将详细介绍sqflite数据库的核心概念、使用方法、最佳实践以及在鸿蒙平台上的实现细节。
核心概念
什么是SQLite
SQLite是一个轻量级的嵌入式关系型数据库,具有以下特点:
- 零配置:无需服务器,无需复杂配置
- 自包含:数据库存储在单个文件中
- 事务支持:支持ACID事务
- SQL标准:支持SQL-92标准
- 跨平台:支持Windows、Linux、macOS、Android、iOS等平台
sqflite的特点
sqflite是Flutter平台上的SQLite插件,具有以下特点:
- 异步操作:所有数据库操作都是异步的
- 事务支持:支持事务操作
- 批处理:支持批量操作
- 数据库版本管理:支持数据库升级和迁移
- 跨平台:支持Android、iOS、鸿蒙等平台
核心组件
sqflite的核心组件包括:
- Database:数据库实例,用于执行SQL语句
- Transaction:事务对象,用于批量操作
- Batch:批处理对象,用于批量执行SQL语句
- OpenDatabaseOptions:数据库打开选项
基本使用
添加依赖
在pubspec.yaml中添加依赖:
dependencies:
sqflite: ^2.3.0
path: ^1.8.3
然后运行flutter pub get安装依赖。
打开数据库
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
Future<Database> openDatabase() async {
final databasePath = await getDatabasesPath();
final path = join(databasePath, 'example.db');
return await openDatabase(
path,
version: 1,
onCreate: (db, version) {
// 创建表
db.execute('''
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
age INTEGER,
created_at TEXT NOT NULL
)
''');
},
);
}
核心代码示例
代码示例1:数据库服务封装
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class SqliteDatabaseService {
static late Database _database;
static const String _databaseName = 'app_database.db';
static const int _databaseVersion = 1;
static final SqliteDatabaseService _instance = SqliteDatabaseService._internal();
factory SqliteDatabaseService() => _instance;
SqliteDatabaseService._internal();
Future<void> init() async {
final databasePath = await getDatabasesPath();
final path = join(databasePath, _databaseName);
_database = await openDatabase(
path,
version: _databaseVersion,
onCreate: _onCreate,
onUpgrade: _onUpgrade,
onOpen: _onOpen,
);
}
Future<void> _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
age INTEGER,
is_active INTEGER DEFAULT 1,
created_at TEXT NOT NULL
)
''');
await db.execute('''
CREATE TABLE posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
title TEXT NOT NULL,
content TEXT,
likes INTEGER DEFAULT 0,
created_at TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
)
''');
await db.execute('CREATE INDEX idx_posts_user_id ON posts(user_id)');
await db.execute('CREATE INDEX idx_posts_created_at ON posts(created_at)');
}
Future<void> _onUpgrade(Database db, int oldVersion, int newVersion) async {
if (oldVersion < 2) {
await db.execute('ALTER TABLE users ADD COLUMN phone TEXT');
}
if (oldVersion < 3) {
await db.execute('CREATE INDEX idx_users_email ON users(email)');
}
}
Future<void> _onOpen(Database db) async {
print('Database opened');
}
Future<int> insertUser(Map<String, dynamic> user) async {
return await _database.insert('users', user);
}
Future<Map<String, dynamic>?> getUser(int id) async {
final List<Map<String, dynamic>> results = await _database.query(
'users',
where: 'id = ?',
whereArgs: [id],
);
return results.isNotEmpty ? results.first : null;
}
Future<List<Map<String, dynamic>>> getAllUsers() async {
return await _database.query('users', orderBy: 'created_at DESC');
}
Future<int> updateUser(int id, Map<String, dynamic> user) async {
return await _database.update(
'users',
user,
where: 'id = ?',
whereArgs: [id],
);
}
Future<int> deleteUser(int id) async {
return await _database.delete(
'users',
where: 'id = ?',
whereArgs: [id],
);
}
Future<List<Map<String, dynamic>>> getUserPosts(int userId) async {
return await _database.query(
'posts',
where: 'user_id = ?',
whereArgs: [userId],
orderBy: 'created_at DESC',
);
}
Future<void> close() async {
await _database.close();
}
}
代码说明:
- 单例模式:确保全局唯一的数据库实例
- 数据库初始化:在init方法中打开数据库
- 表创建:在_onCreate中创建users和posts表
- 索引优化:为posts表创建索引
- 数据库升级:在_onUpgrade中处理版本升级
- CRUD操作:提供完整的增删改查方法
代码示例2:事务操作
class TransactionService {
static Future<void> transferData(int fromUserId, int toUserId, double amount) async {
final db = await SqliteDatabaseService()._database;
await db.transaction((txn) async {
final fromUser = await txn.query(
'users',
where: 'id = ?',
whereArgs: [fromUserId],
);
if (fromUser.isEmpty) {
throw Exception('Source user not found');
}
final fromBalance = fromUser.first['balance'] as double;
if (fromBalance < amount) {
throw Exception('Insufficient balance');
}
await txn.update(
'users',
{'balance': fromBalance - amount},
where: 'id = ?',
whereArgs: [fromUserId],
);
await txn.update(
'users',
{'balance': (await txn.query('users', where: 'id = ?', whereArgs: [toUserId]))
.first['balance'] + amount},
where: 'id = ?',
whereArgs: [toUserId],
);
});
}
}
代码说明:
- 事务操作:使用transaction方法执行事务
- 原子性保证:事务中的所有操作要么全部成功,要么全部失败
- 异常处理:如果任何操作失败,事务会自动回滚
- 数据一致性:确保转账操作的数据一致性
代码示例3:批量操作
class BatchOperationService {
static Future<void> batchInsertUsers(List<Map<String, dynamic>> users) async {
final db = await SqliteDatabaseService()._database;
final batch = db.batch();
for (final user in users) {
batch.insert('users', user);
}
final results = await batch.commit(noResult: true);
print('Inserted ${users.length} users');
}
static Future<void> batchUpdatePosts(List<int> postIds, int likes) async {
final db = await SqliteDatabaseService()._database;
final batch = db.batch();
for (final postId in postIds) {
batch.update(
'posts',
{'likes': likes},
where: 'id = ?',
whereArgs: [postId],
);
}
await batch.commit(noResult: true);
}
static Future<void> batchDeleteOldPosts(int daysToKeep) async {
final db = await SqliteDatabaseService()._database;
final cutoffDate = DateTime.now()
.subtract(Duration(days: daysToKeep))
.toIso8601String();
await db.delete(
'posts',
where: 'created_at < ?',
whereArgs: [cutoffDate],
);
}
}
代码说明:
- 批量插入:使用batch对象批量插入数据
- 批量更新:使用batch对象批量更新数据
- noResult参数:设置为true可以提高性能
- 条件删除:根据条件批量删除数据
代码示例4:复杂查询
class QueryService {
static Future<List<Map<String, dynamic>>> searchUsers(String keyword) async {
final db = await SqliteDatabaseService()._database;
return await db.query(
'users',
where: 'name LIKE ? OR email LIKE ?',
whereArgs: ['%$keyword%', '%$keyword%'],
orderBy: 'created_at DESC',
limit: 20,
);
}
static Future<List<Map<String, dynamic>>> getUsersWithPosts() async {
final db = await SqliteDatabaseService()._database;
return await db.rawQuery('''
SELECT u.*, COUNT(p.id) as post_count
FROM users u
LEFT JOIN posts p ON u.id = p.user_id
GROUP BY u.id
ORDER BY post_count DESC
LIMIT 10
''');
}
static Future<List<Map<String, dynamic>>> getRecentPosts(int limit) async {
final db = await SqliteDatabaseService()._database;
return await db.rawQuery('''
SELECT p.*, u.name as author_name
FROM posts p
JOIN users u ON p.user_id = u.id
ORDER BY p.created_at DESC
LIMIT ?
''', [limit]);
}
static Future<Map<String, dynamic>> getStats() async {
final db = await SqliteDatabaseService()._database;
final userCount = await db.rawQuery('SELECT COUNT(*) as count FROM users');
final postCount = await db.rawQuery('SELECT COUNT(*) as count FROM posts');
final totalLikes = await db.rawQuery('SELECT SUM(likes) as total FROM posts');
return {
'user_count': userCount.first['count'] as int,
'post_count': postCount.first['count'] as int,
'total_likes': totalLikes.first['total'] as int? ?? 0,
};
}
}
代码说明:
- 模糊搜索:使用LIKE操作符进行模糊搜索
- 联表查询:使用LEFT JOIN和JOIN进行联表查询
- 聚合函数:使用COUNT、SUM等聚合函数
- 参数化查询:使用?占位符防止SQL注入
代码示例5:数据库迁移
class MigrationService {
static const String _versionKey = 'database_version';
static Future<void> migrate() async {
final db = await SqliteDatabaseService()._database;
final currentVersion = await _getCurrentVersion(db);
if (currentVersion < 1) {
await _migrateToVersion1(db);
}
if (currentVersion < 2) {
await _migrateToVersion2(db);
}
if (currentVersion < 3) {
await _migrateToVersion3(db);
}
await _setCurrentVersion(db, 3);
}
static Future<int> _getCurrentVersion(Database db) async {
try {
final result = await db.query(_versionKey);
return result.isNotEmpty ? result.first['version'] as int : 0;
} catch (_) {
return 0;
}
}
static Future<void> _setCurrentVersion(Database db, int version) async {
await db.delete(_versionKey);
await db.insert(_versionKey, {'version': version});
}
static Future<void> _migrateToVersion1(Database db) async {
await db.execute('''
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
age INTEGER,
created_at TEXT NOT NULL
)
''');
}
static Future<void> _migrateToVersion2(Database db) async {
await db.execute('ALTER TABLE users ADD COLUMN phone TEXT');
await db.execute('ALTER TABLE users ADD COLUMN is_active INTEGER DEFAULT 1');
}
static Future<void> _migrateToVersion3(Database db) async {
await db.execute('''
CREATE TABLE posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
title TEXT NOT NULL,
content TEXT,
likes INTEGER DEFAULT 0,
created_at TEXT NOT NULL
)
''');
await db.execute('CREATE INDEX idx_posts_user_id ON posts(user_id)');
}
}
代码说明:
- 版本管理:使用_versionKey表存储当前数据库版本
- 增量迁移:根据当前版本执行相应的迁移脚本
- 数据安全:迁移过程中确保数据安全
- 兼容性:支持从任意旧版本升级到最新版本
高级特性
1. 数据库加密
可以使用sqflite_sqlcipher插件实现数据库加密:
import 'package:sqflite_sqlcipher/sqflite_sqlcipher.dart';
Future<Database> openEncryptedDatabase() async {
final databasePath = await getDatabasesPath();
final path = join(databasePath, 'encrypted.db');
return await openDatabase(
path,
password: 'my_secret_password',
version: 1,
onCreate: (db, version) {
db.execute('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)');
},
);
}
2. 数据库备份与恢复
class DatabaseBackupService {
static Future<void> backupDatabase(String backupPath) async {
final db = await SqliteDatabaseService()._database;
await db.close();
final databasePath = await getDatabasesPath();
final sourceFile = File(join(databasePath, 'app_database.db'));
final backupFile = File(backupPath);
await sourceFile.copy(backupPath);
await SqliteDatabaseService().init();
}
static Future<void> restoreDatabase(String backupPath) async {
final db = await SqliteDatabaseService()._database;
await db.close();
final databasePath = await getDatabasesPath();
final targetFile = File(join(databasePath, 'app_database.db'));
final backupFile = File(backupPath);
await backupFile.copy(targetFile.path);
await SqliteDatabaseService().init();
}
}
3. 数据库性能优化
class PerformanceOptimization {
static Future<void> optimizeDatabase() async {
final db = await SqliteDatabaseService()._database;
await db.execute('VACUUM');
await db.execute('ANALYZE');
}
static Future<void> createIndexes() async {
final db = await SqliteDatabaseService()._database;
await db.execute('CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)');
await db.execute('CREATE INDEX IF NOT EXISTS idx_posts_user_id ON posts(user_id)');
await db.execute('CREATE INDEX IF NOT EXISTS idx_posts_created_at ON posts(created_at)');
}
}
在鸿蒙平台的实现
鸿蒙平台适配
sqflite在鸿蒙平台上通过JNI调用鸿蒙平台的SQLite API实现。鸿蒙平台的SQLite与Android平台类似,但有一些差异:
- 存储位置:鸿蒙平台的数据库文件存储在应用沙盒目录下
- 权限配置:需要在
module.json5中配置文件读写权限 - 性能优化:鸿蒙平台的SQLite性能与Android平台相当
存储位置
在鸿蒙平台上,sqflite数据存储在:
/data/data/<包名>/databases/
每个数据库对应一个文件,文件名格式为<数据库名>.db。
鸿蒙平台注意事项
- 权限配置:在
module.json5中配置ohos.permission.WRITE_USER_STORAGE和ohos.permission.READ_USER_STORAGE权限 - 数据库路径:使用
getDatabasesPath()获取数据库目录 - 性能优化:避免在主线程执行耗时查询
性能对比
| 操作 | sqflite | Hive | SharedPreferences |
|---|---|---|---|
| 读取1000条数据 | ~20ms | ~5ms | ~50ms |
| 写入1000条数据 | ~30ms | ~10ms | ~100ms |
| 复杂查询 | 优秀 | 差 | 不支持 |
| 事务支持 | 优秀 | 中等 | 不支持 |
| 内存占用 | 高 | 低 | 中 |
最佳实践
1. 封装数据库操作
将数据库操作封装在Service层,避免直接在UI层使用SQL语句:
class UserRepository {
final SqliteDatabaseService _dbService;
UserRepository(this._dbService);
Future<User> createUser(User user) async {
final id = await _dbService.insertUser(user.toMap());
return user.copyWith(id: id);
}
Future<User?> getUserById(int id) async {
final map = await _dbService.getUser(id);
return map != null ? User.fromMap(map) : null;
}
Future<List<User>> getAllUsers() async {
final maps = await _dbService.getAllUsers();
return maps.map((map) => User.fromMap(map)).toList();
}
}
2. 使用参数化查询
避免SQL注入,使用参数化查询:
// 正确做法
await db.query(
'users',
where: 'email = ?',
whereArgs: [email],
);
// 错误做法
await db.rawQuery('SELECT * FROM users WHERE email = "$email"');
3. 异步操作
所有数据库操作都是异步的,不要阻塞UI线程:
// 正确做法
FutureBuilder<List<User>>(
future: userRepository.getAllUsers(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) => ListTile(
title: Text(snapshot.data![index].name),
),
);
}
return const CircularProgressIndicator();
},
);
4. 数据库关闭
在应用退出时关闭数据库:
void dispose() {
SqliteDatabaseService().close();
super.dispose();
}
5. 错误处理
对数据库操作进行错误处理:
try {
await userRepository.createUser(user);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('User created successfully')),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error: $e')),
);
}
常见问题
Q1: sqflite适合存储大量数据吗?
A:是的。sqflite基于SQLite,适合存储大量关系型数据。
Q2: sqflite支持并发访问吗?
A:sqflite的Database对象不是线程安全的,需要在单个Isolate中使用。
Q3: 如何处理数据库升级?
A:使用onUpgrade回调处理数据库升级。
Q4: sqflite支持外键约束吗?
A:是的,sqflite支持外键约束,但需要在创建表时显式声明。
Q5: 如何提高数据库性能?
A:可以通过创建索引、使用事务、批量操作等方式提高性能。
总结
sqflite是Flutter应用中强大的关系型数据库方案,特别适合存储大量关系型数据和复杂查询场景。它具有完整的SQL功能、事务支持、索引优化等高级特性,API简单直观,易于上手。在鸿蒙平台上,sqflite同样可以正常工作,无需额外配置。
选择合适的数据库方案需要根据数据量、复杂度和性能需求来决定:
| 方案 | 适用场景 | 数据量 | 复杂度 |
|---|---|---|---|
| SharedPreferences | 用户配置、设置 | 小 | 低 |
| Hive | 中等数据量、复杂对象 | 中 | 中 |
| sqflite | 大量数据、关系型数据 | 大 | 高 |
希望本文能帮助你更好地理解和使用sqflite在Flutter应用中进行本地数据持久化。
更多推荐



所有评论(0)