Flutter数据库管理插件quickeydb的使用
Flutter数据库管理插件quickeydb的使用
简介
QuickeyDB 是一个基于 Sqflite 的简单 ORM,灵感来自 ActiveRecord。它通过函数描述符连接到关系型数据库,简化了开发和维护过程。
平台支持
平台 | 支持情况 |
---|---|
Web | ❎ 即将推出 |
MacOS | ✅ 尝试并测试通过 |
Windows | ✅ 尝试并测试通过 |
Linux | ✅ 尝试并测试通过 |
Android | ✅ 尝试并测试通过 |
iOS | ✅ 尝试并测试通过 |
目录
- QuickeyDB简介
- 开始使用QuickeyDB
- 添加QuickeyDB依赖
- 创建用户模型和任务模型
- 创建模式文件
- 初始化数据库
- 简单示例
- 数据访问对象
- 构建查询
- 查找查询
- 数据持久化
- 计算方法
- 辅助方法
- 自定义SQL查询
- 数据表关系
- 属于关系
- 一对一关系
- 一对多关系
- 数据库迁移
- 事务处理
- 批处理支持
- 导入本地数据库
- 数据存储持久化
- 彩色日志
- 平台设置
- Taskan CRUD示例
- 功能请求与错误报告
- 贡献
- 文章和视频
QuickeyDB简介
QuickeyDB 是一个从 ActiveRecord 启发而来的 ORM,依赖于 CREATE TABLE 命令,该命令使用正则表达式分析表定义:
- 表名
- 列定义
- 主键
- 外键
注意:QuickeyDB 是一个运行时库,不依赖于生成大量代码。
开始使用QuickeyDB
1. 添加QuickeyDB依赖
dependencies:
quickeydb: ^x.x.x
2. 创建用户模型和任务模型
// Database/Models/user.dart
import 'task.dart';
class User {
int? id;
String? name;
String? email;
String? phone;
int? age;
Task? task;
User({
this.id,
required this.name,
required this.email,
required this.age,
this.phone,
this.task
});
Map<String, dynamic> toMap() => {
'id': id,
'name': name,
'email': email,
'age': age,
'phone': phone,
'task': task != null ? task!.toMap() : null,
};
Map<String, dynamic> toTableMap() => {
'id': id,
'name': name,
'email': email,
'age': age,
'phone': phone,
};
User.fromMap(Map<String?, dynamic> map)
: id = map['id'],
name = map['name'],
email = map['email'],
age = map['age'],
phone = map['phone'],
task = map['task'] != null ? Task.fromMap(map['task']) : null;
}
// Database/Models/task.dart
import 'user.dart';
class Task {
int? id;
String name;
String body;
int? level;
User? user;
Task({
this.id,
required this.name,
required this.body,
required this.level,
this.user,
});
Task.fromMap(Map<String?, dynamic> map)
: id = map['id'],
name = map['name'],
body = map['body'],
level = map['level'],
user = map['user'] != null ? User.fromMap(map['user']) : null;
Map<String, dynamic> toMap() => {
'id': id,
'name': name,
'body': body,
'level': level,
'user': user != null ? user?.toMap() : null,
};
Map<String, dynamic> toTableMap() => {
'id': id,
'name': name,
'body': body,
'level': level,
};
}
3. 创建模式文件
// Database/schema.dart
import 'package:quickeydb/quickeydb.dart';
import 'Models/user.dart';
import 'Models/task.dart';
class UserSchema extends DataAccessObject<User> {
UserSchema()
: super(
'''
CREATE TABLE user (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL,
phone TEXT,
age INTEGER
)
''',
relations: [
const HasOne<TaskSchema>(),
],
converter: Converter(
encode: (user) => User.fromMap(user),
decode: (user) => user!.toMap(),
decodeTable: (user) => user!.toTableMap(),
),
);
Future<List<User?>> getOldUsers() {
return where({'age >= ?': 18}).toList();
}
Future<List<User>> doRawQuery() async {
// 使用自定义查询
final results = await database!.rawQuery('SELECT * FROM user');
// 返回结果时使用转换器
return results.map((result) => converter.encode(result) as User).toList();
}
}
class TaskSchema extends DataAccessObject<Task> {
TaskSchema()
: super(
'''
CREATE TABLE tasks (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
name TEXT NOT NULL,
body TEXT,
status TEXT,
level INTEGER DEFAULT "1" NOT NULL,
FOREIGN KEY (user_id) REFERENCES user (id)
)
''',
relations: [
const BelongsTo<UserSchema>(),
],
converter: Converter(
encode: (task) => Task.fromMap(task),
decode: (task) => task!.toMap(),
decodeTable: (task) => task!.toTableMap(),
),
);
}
4. 初始化数据库
await QuickeyDB.initialize(
persist: false,
dbVersion: 1,
dataAccessObjects: [
UserSchema(),
TaskSchema(),
],
dbName: 'tascan_v1',
);
5. 简单示例
await QuickeyDB.getInstance<UserSchema>()?.create(
User(
name: 'Kenzy Limon',
email: 'itskenzylimon@gmail.com',
phone: '+254 712345678',
task: Task(name: 'Create Package', body: 'Create a Flutter DB Package')
),
);
数据访问对象
构建查询
/// SELECT * FROM user
QuickeyDB.getInstance<UserSchema>()!.all; // | 返回一个列表<T>
/// SELECT id FROM user
QuickeyDB.getInstance<UserSchema>()!.select(['id']).toList(); // 返回一个列表<T>
/// SELECT * FROM user WHERE name = 'Sam' OR name = 'Mike'
QuickeyDB.getInstance<UserSchema>()!.where({'name': 'Kenzy Limon'}).or({'age': '21'}).toList();
/// 若要使用其他操作,只需将其传递给属性
// SELECT * FROM user where age >= 18
QuickeyDB.getInstance<UserSchema>()!.where({'age >= ?': 18}).toList();
// SELECT * FROM user ORDER BY name DESC
QuickeyDB.getInstance<UserSchema>()!.order(['age']).toList();
// SELECT * FROM user GROUP BY name HAVING LENGTH(name) > 5
QuickeyDB.getInstance<UserSchema>()!.group(['name']).having('LENGTH(name) > 5').toList();
// SELECT * FROM user LIMIT 50 OFFSET 100
QuickeyDB.getInstance<UserSchema>()!.limit(1).offset(10).toList();
// SELECT DISTINCT * FROM user
QuickeyDB.getInstance<UserSchema>()!.distinct().toList();
包含查询
// SELECT * FROM user
// SELECT * FROM task WHERE id IN (1)
QuickeyDB.getInstance<UserSchema>()?.includes([TaskSchema]).toList()
// [User(id: 1, name: 'Kenzy Limon',... task: [Task(id: 1, name: 'Complete ORM', body: 'Do nice Documentation')])]
联合查询
// SELECT
// task.*,
// user.id AS user_id,
// user.name AS user_name,
// FROM task
// INNER JOIN user ON user.id = task.user_id
QuickeyDB.getInstance<TaskSchema>()!.joins([UserSchema]).toList();
// [Task(id: 1, name: 'Complete ORM', body: 'Do nice Documentation',... user: User(id: 1, name: 'Kenzy Limon',...))]
查找查询
// SELECT * FROM user WHERE name = 'Kenzy Limon' LIMIT 1
QuickeyDB.getInstance<UserSchema>()!.isExists({'name': 'John Doe'}); // true
// SELECT * FROM user WHERE id = 1 LIMIT 1
QuickeyDB.getInstance<UserSchema>()!.find(1); // User
// SELECT * FROM user WHERE name = 'Mike' LIMIT 1
QuickeyDB.getInstance<UserSchema>()!.findBy({'name': 'Jane Doe'}); // User
// SELECT * FROM user
QuickeyDB.getInstance<UserSchema>()!.first; // 第一项
// SELECT * FROM user
QuickeyDB.getInstance<UserSchema>()!.last; // 最后一项
// SELECT * FROM user LIMIT 3
QuickeyDB.getInstance<UserSchema>()!.take(10);
数据持久化
final user = User(id: 1, name: 'Kenzy Limon', age: 21,...);
// INSERT INTO user (id, name, age,...) VALUES (1, 'Kenzy Limon', 21,...)
QuickeyDB.getInstance<UserSchema>()!.create(user); // | createAll
// 也可以使用 `insert`,它接受映射
QuickeyDB.getInstance<UserSchema>()!.insert(user.toMap()); // insertAll
// UPDATE user SET name = 'Kenzy Limon', age = 21 WHERE id = 1
QuickeyDB.getInstance<UserSchema>()!.update(user..name = 'John Doe');
// DELETE FROM user WHERE id = 1
QuickeyDB.getInstance<UserSchema>()!.delete(user);
// DELETE FROM user WHERE id = 1
QuickeyDB.getInstance<UserSchema>()!.destroy(1); // (truncate)
数据库关系
QuickeyDB 提供了内置的一对一、一对多和属于关系的支持。这些关系在定义时会自动构建底层的 SQL 查询。
一对一关系
// INSERT INTO user (id, name, age,...) VALUES (NULL, 'Jane Doe', 25,...);
// INSERT INTO task (id, name, user_id,...) VALUES (NULL, 'Test Cases', 1);
QuickeyDB.getInstance<TaskSchema>()!.create(
Task(
name: 'Test Cases',...
user: User(
name: 'Jane Doe', age: 25
),
),
)
一对多关系
// INSERT INTO user (id, name, age) VALUES (NULL, 'John Doe', 10);
// INSERT INTO task (id, name, user_id) VALUES (NULL, 'Write Documentation', 1);
QuickeyDB.getInstance<UserSchema>()!.create(
User(
name: 'Jane Doe',
age: 25,...
task: [
Task(name: 'Test Cases'),...
],
),
)
计算方法
/// SELECT COUNT(*) FROM user
QuickeyDB.getInstance<UserSchema>()!.count(); // 1001
/// SELECT COUNT(email) FROM user
QuickeyDB.getInstance<UserSchema>()!.count('email'); // 600
/// SELECT AVG(age) FROM user
QuickeyDB.getInstance<UserSchema>()!.average('age'); // 35
/// SELECT id FROM user
QuickeyDB.getInstance<UserSchema>()!.ids; // [1, 2, 3,...]
/// SELECT MAX(age) FROM user
QuickeyDB.getInstance<UserSchema>()!.maximum('age'); // 69
/// SELECT MIN(age) FROM user
QuickeyDB.getInstance<UserSchema>()!.minimum('age'); // 18
/// SELECT name, age FROM user LIMIT 1
QuickeyDB.getInstance<UserSchema>()!.pick(['name', 'age']); // ['John Doe', 96]
/// SELECT name FROM user
QuickeyDB.getInstance<UserSchema>()!.pluck(['name', 'age']); // [['John Doe', '96'],...]
/// SELECT SUM(age) FROM user
QuickeyDB.getInstance<UserSchema>()!.sum('age'); // 404
辅助方法
/// 将查询转换为列表
QuickeyDB.getInstance<UserSchema>()!.foo().bar().toList(); // [User,...]
/// 将查询转换为映射
QuickeyDB.getInstance<UserSchema>()!.foo().bar().toMap(); // [{id: 1, name: 'Mike', age: 10}, ...]
/// 别名为 count > 0
QuickeyDB.getInstance<UserSchema>()!.foo().bar().isEmpty; // | false
数据表关系
确保在表之间添加 FOREIGN KEY。
属于关系
// Database/schema.dart
class TaskSchema extends DataAccessObject<Task> {
TaskSchema()
: super(
'''
CREATE TABLE tasks (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
name TEXT NOT NULL,
body TEXT,
status TEXT,
level INTEGER DEFAULT "1" NOT NULL,
FOREIGN KEY (user_id) REFERENCES user (id)
)
''',
relations: [
const BelongsTo<UserSchema>(),
],
converter: Converter(
encode: (task) => Task.fromMap(task),
decode: (task) => task!.toMap(),
decodeTable: (task) => task!.toTableMap(),
),
);
}
一对一关系
// Database/schema.dart
class TaskSchema extends DataAccessObject<Task> {
TaskSchema()
: super(
'''
CREATE TABLE tasks (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
name TEXT NOT NULL,
body TEXT,
status TEXT,
level INTEGER DEFAULT "1" NOT NULL,
FOREIGN KEY (user_id) REFERENCES user (id)
)
''',
relations: [
const HasOne<UserSchema>(),
],
converter: Converter(
encode: (task) => Task.fromMap(task),
decode: (task) => task!.toMap(),
decodeTable: (task) => task!.toTableMap(),
),
);
}
一对多关系
// Database/schema.dart
class UserSchema extends DataAccessObject<User> {
UserSchema()
: super(
'''
CREATE TABLE user (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL,
phone TEXT,
age INTEGER
)
''',
relations: [
const HasMany<TaskSchema>(),
],
converter: Converter(
encode: (user) => User.fromMap(user),
decode: (user) => user!.toMap(),
decodeTable: (user) => user!.toTableMap(),
),
);
}
自定义SQL查询
class UserSchema extends DataAccessObject<User> {
...
Future<List<User?>> getOldUsers() {
return where({'age >= ?': 18}).toList();
}
...
}
你也可以通过访问 database
对象来使用更复杂的查询。
class UserSchema extends DataAccessObject<User> {
...
Future<List<User>> doRawQuery() async {
// 使用自定义查询
final results = await database!.rawQuery('SELECT * FROM user');
// 返回结果时使用转换器
return results.map((result) => converter.encode(result) as User).toList();
}
...
}
数据存储持久化
要使用持久化数据库,将 persist
属性设置为 true
。
final quickeyDB = QuickeyDB(
persist: true,
)
导入本地数据库
- 将现有数据库复制到
assets/database.db
- 在
pubspec.yaml
中添加路径
+ flutter:
+ assets:
+ - assets/database.db
- 将
importDB
属性设置为true
final quickeyDB = QuickeyDB(
importDB: true,
)
- 运行
数据库迁移
因为我们依赖于 CREATE TABLE 命令,所以最好的解决方案是通过创建新表并移动所有数据来强制迁移。
- 修改你的 SQL 命令,例如:
class UserSchema extends DataAccessObject<User> {
UserSchema()
: super(
'''
CREATE TABLE user (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL,
phone TEXT,
address TEXT,
age INTEGER
)
''',
relations: [
const HasOne<TaskSchema>(),
],
converter: Converter(
encode: (user) => User.fromMap(user),
decode: (user) => user!.toMap(),
decodeTable: (user) => user!.toTableMap(),
),
);
}
- 不要忘记更改数据库版本。
final quickeyDB = QuickeyDB.initialize!(
dbVersion: 2, // 任何版本
);
注意:除非你传递了默认值,否则在迁移时永远不要添加 NOT NULL
列。
事务处理
事务中的调用必须仅使用事务对象 (txn
),避免在事务内部使用 database
或 QuickeyDB.getInstance
,因为这会导致数据库死锁。
请注意,onCreate
、onUpgrade
和 onDowngrade
回调已经内部包裹在一个事务中,因此不需要在这些回调内包装你的语句。
await QuickeyDB.getInstance!.database!.transaction((txn) async {
txn.insert('users', { mapped data }, conflictAlgorithm: ConflictAlgorithm.replace);
txn.delete('users', where: 'id = ?', whereArgs: [id]);
txn.update('users', { mapped data });
txn.rawDelete('DELETE FROM users WHERE name = ?', ['Kenzy Limon']);
txn.rawDelete('DELETE FROM users WHERE name = ?', ['Kenzy Limon']);
txn.rawDelete('DELETE FROM users WHERE name = ?', ['Kenzy Limon']);
txn.rawQuery('SELECT COUNT(*) FROM users');
await txn.execute('CREATE TABLE task_types (id INTEGER PRIMARY KEY)');
});
批处理支持
var batch = QuickeyDB.getInstance!.database!.batch();
batch.insert('users', {'name': 'Kenzy'});
batch.update('users', {'name': 'Kenzy Limon'}, where: 'name = ?', whereArgs: ['Kenzy']);
batch.delete('users', where: 'name = ?', whereArgs: ['Kenzy']);
var results = await batch.commit();
获取每个操作的结果会有成本(插入时返回 ID,更新和删除时返回更改数)。
在 Android 上,会执行额外的 SQL 请求。如果你不关心结果且担心大批次的性能,可以使用:
await batch.commit(noResult: true);
注意,在事务期间,批处理不会提交直到事务提交。
await database.transaction((txn) async {
var batch = txn.batch();
// ...
await batch.commit();
});
实际的提交将在事务提交时发生。
默认情况下,批处理会在遇到错误时停止(通常会撤销未提交的更改)。要改变这种行为,你必须将 continueOnError
设置为 false
。
await batch.commit(continueOnError: true);
彩色日志
注意:在调试模式下,默认情况下 logger
是启用的。如果你想禁用它,只需将 debugging
属性设置为 false
。
final quickeyDB = QuickeyDB.initialize!(
debugging: false, // 任何版本
);
支持的数据类型
DateTime
不是 SQLite 的支持类型。你可以将其存储为 int(毫秒时间戳)或字符串(ISO8601格式)。
bool
也不是 SQLite 的支持类型。你可以使用 INTEGER
和 0 和 1 的值。
INTEGER
- Dart 类型:
int
- 支持值: 从 -2^63 到 2^63 - 1
REAL
- Dart 类型:
num
TEXT
- Dart 类型:
String
BLOB
- Dart 类型:
Uint8List
平台设置
Linux
libsqlite3 和 libsqlite3-dev Linux 包是必需的。
Ubuntu 一次性设置(以 root 用户身份运行):
dart tool
/
linux_setup.dart
或者
sudo apt-get -y install libsqlite3-0 libsqlite3-dev
MacOS
应该可以直接工作。
Web
将 sqflite_sw.js
和 sqlite3.wasm
从示例的 web 文件夹复制到你的根目录下的 web 文件夹。然后按如下方式导入数据库:
import 'package:quickeydb/quickeydb.dart' if (dart.library.html) 'package:quickeydb/quickeywebdb.dart';
Windows
在调试模式下应直接工作(sqlite3.dll
已包含)。
在发布模式下,将 sqlite3.dll
放在同一文件夹中作为你的可执行文件。
Taskan CRUD 示例
功能请求与错误报告
功能请求和错误报告在 问题追踪器。
贡献
Quickey Database ORM 是一个开源项目,欢迎贡献 - 如果你遇到任何问题,请随时 创建新问题,或提交 拉取请求。有关社区贡献指南,请查看 行为准则。
如果提交拉取请求,请确保满足以下标准:
- 代码文件必须格式良好(运行
flutter format .
)。 - 测试必须通过(运行
flutter test
)。强烈建议添加新的测试用例来验证你的更改。 - 实现不得添加任何项目依赖。
- 项目必须包含零警告。运行
flutter analyze
必须返回零个问题。 - 确保文档字符串保持最新。新功能添加必须包括文档字符串。
额外信息
此包有三个核心依赖项,包括核心 SQLite 包。
- sqflite_common_ffi // 用于桌面应用程序
- sqflite
- collection
更多关于Flutter数据库管理插件quickeydb的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter数据库管理插件quickeydb的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
quickeydb
是一个轻量级的 Flutter 插件,用于在 Flutter 应用中管理 SQLite 数据库。它提供了一个简单易用的 API,使得开发者可以轻松地进行数据库的创建、查询、插入、更新和删除操作。
以下是如何在 Flutter 项目中使用 quickeydb
插件的步骤:
1. 添加依赖
首先,在 pubspec.yaml
文件中添加 quickeydb
插件的依赖:
dependencies:
flutter:
sdk: flutter
quickeydb: ^1.0.0 # 请使用最新的版本号
然后运行 flutter pub get
来安装依赖。
2. 初始化数据库
在使用 quickeydb
之前,需要先初始化数据库。通常,你可以在应用的 main
函数中进行初始化。
import 'package:flutter/material.dart';
import 'package:quickeydb/quickeydb.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await QuickeyDB.init(); // 初始化数据库
runApp(MyApp());
}
3. 创建数据模型
quickeydb
使用 Dart 类来表示数据库中的表。你需要定义一个类,并使用 [@Table](/user/Table)
和 [@Column](/user/Column)
注解来标记表和列。
import 'package:quickeydb/quickeydb.dart';
[@Table](/user/Table)(name: 'users')
class User {
[@Column](/user/Column)(name: 'id', isPrimaryKey: true, autoIncrement: true)
int? id;
[@Column](/user/Column)(name: 'name')
String? name;
[@Column](/user/Column)(name: 'age')
int? age;
User({this.id, this.name, this.age});
}
4. 创建表
在数据库初始化后,你需要创建表。可以通过调用 QuickeyDB.createTable
方法来创建表。
await QuickeyDB.createTable(User);
5. 插入数据
你可以使用 QuickeyDB.insert
方法将数据插入到表中。
User user = User(name: 'John Doe', age: 30);
await QuickeyDB.insert(user);
6. 查询数据
你可以使用 QuickeyDB.query
方法来查询表中的数据。
List<User> users = await QuickeyDB.query(User);
for (var user in users) {
print('User: ${user.name}, Age: ${user.age}');
}
你还可以使用 where
条件来过滤查询结果:
List<User> users = await QuickeyDB.query(User, where: 'age > ?', whereArgs: [25]);
7. 更新数据
你可以使用 QuickeyDB.update
方法来更新表中的数据。
User user = User(id: 1, name: 'Jane Doe', age: 25);
await QuickeyDB.update(user);
8. 删除数据
你可以使用 QuickeyDB.delete
方法来删除表中的数据。
await QuickeyDB.delete(User, where: 'id = ?', whereArgs: [1]);
9. 删除表
如果你需要删除表,可以使用 QuickeyDB.dropTable
方法。
await QuickeyDB.dropTable(User);
10. 关闭数据库
在应用退出时,可以关闭数据库连接。
await QuickeyDB.close();
完整示例
以下是一个完整的示例,展示了如何使用 quickeydb
插件进行数据库操作:
import 'package:flutter/material.dart';
import 'package:quickeydb/quickeydb.dart';
[@Table](/user/Table)(name: 'users')
class User {
[@Column](/user/Column)(name: 'id', isPrimaryKey: true, autoIncrement: true)
int? id;
[@Column](/user/Column)(name: 'name')
String? name;
[@Column](/user/Column)(name: 'age')
int? age;
User({this.id, this.name, this.age});
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await QuickeyDB.init();
await QuickeyDB.createTable(User);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('QuickeyDB Example')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () async {
User user = User(name: 'John Doe', age: 30);
await QuickeyDB.insert(user);
print('User inserted');
},
child: Text('Insert User'),
),
ElevatedButton(
onPressed: () async {
List<User> users = await QuickeyDB.query(User);
for (var user in users) {
print('User: ${user.name}, Age: ${user.age}');
}
},
child: Text('Query Users'),
),
ElevatedButton(
onPressed: () async {
User user = User(id: 1, name: 'Jane Doe', age: 25);
await QuickeyDB.update(user);
print('User updated');
},
child: Text('Update User'),
),
ElevatedButton(
onPressed: () async {
await QuickeyDB.delete(User, where: 'id = ?', whereArgs: [1]);
print('User deleted');
},
child: Text('Delete User'),
),
],
),
),
),
);
}
}