Flutter数据库生成插件sqflite_gen的使用
Flutter数据库生成插件sqflite_gen的使用
Sqflite Gen
开源代码生成器,用于统一的SQLite数据库访问,适用于Flutter和Dart。
概述
此包的目标是通过处理sqflite包重复的数据库访问层重写任务来支持开发者。它解析SQL创建脚本并生成模型、提供器和常量,以便访问所有表。它还创建了一个数据库访问层,并在应用程序首次运行时自动创建所有表。
它自动处理布尔值、Uint8List和DateTime类型的自动类型映射。
生成的源遵循由sqflite提供的最佳原则(参见“SQL助手”部分)。
安装
❗ 要开始使用Sqflite Gen,必须在机器上安装Dart SDK。
通过dart pub add
安装:
dart pub add sqflite_gen
然后运行:
dart pub get
或
flutter pub get
现在SqfliteGen将通过运行以下命令为您生成数据库访问层文件:
dart run build_runner build
配置
为了让包生成所需的源代码,请在应用的资源目录中放置一个以.sql
结尾的文件。该文件必须包括数据库的所有create table
语句。
所有生成的文件将放在/下的lib/db
目录中。
概述
支持自增主键。
对于每个表,将在lib/db/tables
下创建一个子目录。该包为每个表创建一个数据模型、表访问提供器和用于统一访问表和字段的常量。
模型文件与数据库表同名。它表示一个表,包含该表的每一列的属性。它还包含将映射到Map和从Map转换的方法,以及一个copyWith
方法来创建一个新的克隆。
提供器类与数据库表同名,并带有后缀Provider
。它通过提供CRUD操作的基本访问方法允许对表的基本访问:insert
、get
、update
和delete
。为了方便起见,它还提供了一个返回表中所有记录的getAll
方法。
进一步的常量被创建用于封装表名和所有列名。
使用示例
导入文件以获取数据库访问权限(替换example_app
为您的应用引用)
import 'package:example_app/db/database.dart';
import 'package:example_app/db/db.dart';
打开数据库
打开数据库也包含一个基本的迁移机制。这目前仅用于在第一次打开数据库时动态创建所有表。之后数据库结构不再改变。
const dbName = 'test.db';
late Database database;
.
.
.
database = await openDatabaseWithMigration(dbName);
许多应用程序只使用一个数据库,且永远不会关闭它(当应用程序终止时会自动关闭)。如果您想释放资源,可以关闭数据库。
await database.close();
访问数据库表
每个表都由一个模型和一个提供器类表示。它们以类型安全的方式封装访问。
对于以下示例,我们假设数据库表如下所示:
CREATE TABLE Test(
id INTEGER PRIMARY KEY AUTOINCREMENT,
text VARCHAR NOT NULL
);
插入
final record = Test(text: 'This is a test');
final table = TestProvider(database);
final insertedRecord = await table.insert(record);
log(insertedRecord.id.toString());
请注意,insert
方法自动处理自增列id
(当未显式提供值时)。insert
还返回包含数据库给定的自增列id
值的原始record
的副本。
查询
get
方法期望一个主键值。
final table = TestProvider(database);
final record = await table.get(1);
log(insertedRecord.text);
更新
final table = TestProvider(database);
final record = await table.get(1);
final changedRecord = record.copyWith(text: 'Changed text');
await table.update(changedRecord);
删除
delete
方法期望一个主键值。
final table = TestProvider(database);
final success = await table.delete(1);
log(success ? 'Successfully deleted' : 'Failing deleting record');
处理空值
标记为可为空的列在模型类中表示为可为空的字段。
CREATE TABLE Test(
id INTEGER PRIMARY KEY AUTOINCREMENT,
text VARCHAR
);
这对copyWith
方法来说是一个问题,因为给定的值现在可以代表三种不同的动作:
- 设置值为新值
- 将值设置为null
- 保持值不变
为了支持这一点,可为空的字段在copyWith
方法中被包装:
// 初始化模型
final record = Test(text: 'Original text');
log(record.text); // 打印 Original text
// 更改可为空的列 text 为不同的字符串
final changedRecord = record.copyWith(text: Wrapped.value('New text'));
log(record.text); // 打印 New text
// 保持列 text 的值
final otherRecord = record.copyWith(); // 相当于: record.copyWith(text: null)
log(record.text); // 打印 Original text
// 将可为空的列 text 更改为 null 值
final nulledRecord = record.copyWith(text: Wrapped.value(null));
log(record.text); // 打印 <null>
支持的SQLite类型
目前尚未对值进行有效性检查,因此请避免使用不支持的类型 SQLite文档。
示例类型名 | 生成的Dart字段类型 |
---|---|
INT, INTEGER, TINYINT, SMALLINT, MEDIUMINT, BIGINT, UNSIGNED BIG INT, INT2, INT8 | int |
CHARACTER, VARCHAR, VARYING CHARACTER, NCHAR, NATIVE CHARACTER, NVARCHAR, TEXT, CLOB, STRING | String |
BLOB | Uint8List |
BOOL | bool |
DATE, DATETIME | double |
REAL, DOUBLE, DOUBLE PRECISION, FLOAT, NUMERIC, DECIMAL | DateTime |
更多关于支持类型的信息 这里。
持续集成
Sqflite Gen 使用由 Very Good Workflows 提供的 GitHub Actions 工作流。
默认情况下,在每次拉取请求和推送时,CI 会格式化、检查和测试代码。这确保了随着添加功能或更改代码,代码保持一致且行为正确。该项目使用 Very Good Analysis 作为严格的分析选项。使用 Very Good Workflows 来强制执行代码覆盖率。
运行测试
要运行所有单元测试:
dart pub global activate coverage 1.2.0
dart test --coverage=coverage
dart pub global run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info
要查看生成的覆盖率报告,您可以使用 lcov。
# 生成覆盖率报告
genhtml coverage/lcov.info -o coverage/
# 打开覆盖率报告
open coverage/index.html
示例代码
import 'dart:typed_data';
import 'package:example_app/db/database.dart';
import 'package:example_app/db/db.dart';
import 'package:example_app/my_list_item.dart';
import 'package:flutter/material.dart';
import 'package:uuid/uuid.dart';
import 'package:sqflite/sqflite.dart';
const dbName = 'test.db';
late Database database;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sqflite_Gen Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Sqflite_Gen Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
[@override](/user/override)
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<Test> _list = [];
final uuid = const Uuid();
[@override](/user/override)
void initState() {
super.initState();
_initDatabase();
}
void _initDatabase() async {
database = await openDatabaseWithMigration(dbName);
final table = TestProvider(database);
_updateList(table);
}
void _addRecord() async {
final guid = uuid.v4();
final newRecord = Test(
text: guid,
number: _list.length,
numeric: _list.length / 2,
date: DateTime.now(),
isChecked: _list.length % 2 != 0,
anything: Uint8List.fromList(guid.codeUnits),
);
final table = TestProvider(database);
await table.insert(newRecord);
_updateList(table);
}
void _updateRecord(int id) async {
final record = _list.firstWhere((item) => item.id == id);
final table = TestProvider(database);
await table.update(record.copyWith(date: DateTime.now()));
_showSnackBar('$id updated');
_updateList(table);
}
void _deleteRecord(int id) async {
final table = TestProvider(database);
await table.delete(id);
_showSnackBar('$id deleted');
_updateList(table);
}
void _updateList(TestProvider table) async {
final allRecords = await table.getAll();
setState(() {
_list = allRecords;
});
}
void _showSnackBar(String text) {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(text),
),
);
}
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: ListView.separated(
separatorBuilder: (_, __) => const Divider(
height: 1,
indent: 10,
endIndent: 10,
),
itemCount: _list.length,
scrollDirection: Axis.vertical,
itemBuilder: (context, index) => MyListItem(
key: ValueKey(_list[index].id),
record: _list[index],
onDelete: _deleteRecord,
onUpdate: _updateRecord,
),
),
floatingActionButton: FloatingActionButton(
onPressed: _addRecord,
tooltip: 'Add record to database',
child: const Icon(Icons.add),
),
);
}
}
更多关于Flutter数据库生成插件sqflite_gen的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter数据库生成插件sqflite_gen的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
sqflite_gen
是一个用于 Flutter 的代码生成插件,它可以帮助你自动生成 SQLite 数据库的代码,从而简化数据库操作。它基于 sqflite
库,提供了类型安全和更简洁的代码生成功能。
以下是使用 sqflite_gen
的基本步骤:
1. 添加依赖
首先,你需要在 pubspec.yaml
文件中添加 sqflite_gen
和 build_runner
依赖:
dependencies:
flutter:
sdk: flutter
sqflite: ^2.0.0+4
dev_dependencies:
build_runner: ^2.1.0
sqflite_gen: ^1.0.0
然后运行 flutter pub get
来获取依赖。
2. 创建数据库模型
接下来,你需要定义数据库的表和模型。你可以使用 @Database
和 @Table
注解来定义数据库和表。
import 'package:sqflite_gen/sqflite_gen.dart';
@Database(name: 'my_database.db', version: 1)
class MyDatabase {
@Table(name: 'users')
class User {
@Column(primaryKey: true, autoIncrement: true)
int id;
@Column(type: ColumnType.text)
String name;
@Column(type: ColumnType.integer)
int age;
}
}
3. 生成代码
运行以下命令来生成数据库代码:
flutter pub run build_runner build
这将会根据你定义的模型生成相应的数据库操作代码。
4. 使用生成的代码
生成的代码会包含一个 MyDatabase
类,你可以使用它来执行数据库操作。
import 'package:flutter/material.dart';
import 'my_database.g.dart'; // 生成的代码
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final database = MyDatabase();
await database.open();
// 插入数据
await database.users.insert(User()
..name = 'John Doe'
..age = 30);
// 查询数据
final users = await database.users.query();
for (final user in users) {
print('User: ${user.name}, Age: ${user.age}');
}
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Sqflite Gen Example'),
),
body: Center(
child: Text('Check the console for database output.'),
),
),
);
}
}
5. 其他操作
生成的代码还支持其他常见的数据库操作,如更新、删除等。你可以通过生成的 MyDatabase
类来访问这些操作。
例如,更新数据:
await database.users.update(user);
删除数据:
await database.users.delete(user);
6. 关闭数据库
当不再需要数据库时,记得关闭它:
await database.close();