Flutter怎么用ORM框架简化本地数据库管理
这篇“Flutter怎么用ORM框架简化本地数据库管理”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Flutter怎么用ORM框架简化本地数据库管理”文章吧。
floor 简介
floor
是基于 sqflite
的一个轻量级的 ORM 框架,通过注解和代码生成可以将数据库数据直接映射为实体类对象。floor
内置了很多操作数据库的方法,比如增删改查,让我们快速接入数据库。同时,也可以在注解中编写 SQL来实现复杂的数据库查询,比如 IN
查询、数据统计等等。通过注解和代码生成能够减少大量手写代码,提高我们的开发效率和代码的可维护性。 floor 需要引入的开发依赖如下,都是用于基于注解生成代码。
dev_dependencies: flutter_test: sdk: flutter # ... floor_generator: ^1.4.1 build_runner: ^2.3.3
接下来我们就以之前的备忘录为例,来看看使用 floor
后的改善。
ORM 映射
我们之前的备忘录类 Memo
需要自己编写 fromJson
和 toJson
方法来实现数据库数据到实体类对象的转换。此外,遇到 SQLite 不支持的数据类型(如 DateTime
和 List<String>
)时,还需要处理转换代码。我们来看 floor
如何处理。 floor
将数据库操作分为实体类和 DAO,实体类与数据库的映射通过注解完成。例如我们的 Memo
类,调整后的代码如下所示。
@entityclass Memo { @PrimaryKey(autoGenerate: true) final int? id; String title; String content; @ColumnInfo(name: 'created_time') DateTime createdTime; @ColumnInfo(name: 'modified_time') DateTime modifiedTime; List<String> tags; Memo({ this.id, required this.title, required this.content, required this.createdTime, required this.modifiedTime, required this.tags, });}
这里说明一下常见的注解:
@entity
:表示这是一个实体类,会和数据库的某个数据表映射,默认表名就是类名。如果要手动指定表名,可以使用@Entity(tableName: tableName)
通过tableName
指定数据表名称。floor 会自动根据@entity
注解生成创建数据表的 SQL 语句。@primaryKey
:表示字段为主键,如果需要使用自增主键,可以使用@PrimaryKey(autoGenerate: true)
。@ColumnInfo(name: name)
:设置实体类成员属性和数据表字段的映射关系,默认 floor 使用的数据表字段名称和类成员属性名称一致,如果需要指定数据表字段名,就可以使用这个注解。@ignore
:忽略某个成员属性,即该属性不产生相应的数据表字段。注意,通过 get 方法产生的计算属性默认就会被忽略,例如长方形面积double get area => width * height
。
DAO 用于从数据库查询数据并转换为实体类对象,从数据库查询数据和转换的代码通过注解直接生成。DAO 提供了基础的插入、更新和删除方法,这些方法可以通过注解@insert
、@update
和@delete
完成,不需要编写 SQL。 同时,对于插入和更新可以设置冲突策略,策略可以是中止(abort)、回滚(rollback)、替换(replace)、忽略(ignore)、失败(fail)。其中除了替换以外,其他都是和数据库事务有关。
@daoabstract class MemoDao { @Query('SELECT * FROM Memo ORDER BY modified_time DESC') Future<List<Memo>> findAllMemos(); @Query( 'SELECT * FROM Memo WHERE title LIKE :searchKey OR content LIKE :searchKey ORDER BY modified_time DESC') Future<List<Memo>> findMemoWithSearchKey(String searchKey); @Query('SELECT * FROM Memo WHERE id = :id') Stream<Memo?> findMemoById(int id); @insert Future<void> insertMemo(Memo memo); @Update(onConflict: OnConflictStrategy.replace) Future<void> updateMemo(Memo memo); @delete Future<void> deleteMemo(Memo memo);}
转换器
使用 floor
可以统一 Dart 数据类型到 SQLite 字段的转换方式。通过定义不同的类型转换器TypeConverter
实现数据库和Dart
数据类型的转换,从而避免了每个实体类都要单独编写转换代码。比如我们在备忘录用到了两个类型 DateTime
和 List<String>
就定义了对应的转换器。
class StringListConverter extends TypeConverter<List<String>, String> { @override List<String> decode(String databaseValue) { return databaseValue.isNotEmpty ? databaseValue.split('|') : []; } @override String encode(List<String> value) { return value.join('|'); }}class DateTimeConverter extends TypeConverter<DateTime, int> { @override DateTime decode(int databaseValue) { return DateTime.fromMillisecondsSinceEpoch(databaseValue); } @override int encode(DateTime value) { return value.millisecondsSinceEpoch; }}
使用转换器只需要在定义数据库FloorDatabase
的抽象类的时候引入到注解@TypeConverters
就可以了。
@TypeConverters([StringListConverter, DateTimeConverter])@Database(version: 1, entities: [Memo])abstract class MemoDatabase extends FloorDatabase { MemoDao get memoDao;}
代码改造
通常来说 DAO 对象会在很多地方共用,适合使用单例方式来构造。这里我们在App启动的时候就使用 GetIt来实现MemoDao 的单例注册。
Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); final database = await $FloorMemoDatabase.databaseBuilder('app_database.db').build(); final dao = database.memoDao; getIt.registerSingleton<MemoDao>(dao, signalsReady: true); runApp(const MyApp());}
这里调用ensureInitialized
这个方法是保证 Flutter 和原生交互的部分已经完成,因为在 sqflite 中需要使用原生的文件存储。 备忘录列表的代码改造涉及数据操作的有两处,分别是列表刷新和删除备忘录。列表模糊搜索时需要自己组装模糊搜索的字符,比如我们这里使用了百分号将搜索关键词包裹实现任意匹配。删除备忘录需要根据是否有搜索调用不同的方法,这是因为对应的 SQL 不同。
void _refreshMemoList({String? searchKey}) async { List<Memo> memoList = searchKey == null ? await GetIt.I<MemoDao>().findAllMemos() : await GetIt.I<MemoDao>().findMemoWithSearchKey('%$searchKey%'); setState(() { _memoList = memoList; });}
删除就非常简单了,直接调用删除方法就好了。
void _deleteMemo(Memo memo) async { final confirmed = await _showDeleteConfirmationDialog(memo); if (confirmed != null && confirmed) { await GetIt.I<MemoDao>().deleteMemo(memo); _refreshMemoList(); if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text('已删除 "${memo.title}"'), duration: const Duration(seconds: 2), )); } }
添加备忘录的页面只需要更改保存备忘录的方法,而且因为不需要再对时间做转换,方法更为简洁。
Future<void> _saveMemo(BuildContext context) async { var memo = Memo( title: _title, content: _content, createdTime: DateTime.now(), modifiedTime: DateTime.now(), tags: _tags); // 保存备忘录 await GetIt.I<MemoDao>().insertMemo(memo);}
编辑备忘录页面也类似,调用 updateMemo
方法即可完成保存。
Future<void> _saveMemo(BuildContext context) async { widget.memo.title = _title; widget.memo.content = _content; widget.memo.modifiedTime = DateTime.now(); // 保存备忘录 await GetIt.I<MemoDao>().updateMemo(widget.memo);}
总结
代码已经提交到:本地存储相关代码,注意如果更改了 ORM 相关的类,需要运行下面的命令重新生成代码。
flutter packages pub run build_runner build
可以看到,通过 floor
这样的 ORM 框架可以让整个本地数据库管理的代码更为简洁,复用性更高。如果说是本地数据存储比较复杂的,推荐使用 ORM 框架来管理。
以上就是关于“Flutter怎么用ORM框架简化本地数据库管理”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注编程网行业资讯频道。
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341