当前位置: 首页 > 工具软件 > Drift > 使用案例 >

Flutter 全平台 sqlite/sqlcipher orm 框架 drift(原moor)的使用

莫典
2023-12-01

前言

Flutter 目前比较好用的 sqlite 数据库 orm 框架就是drift (以前叫作moor),由于dart语言不支持反射,这个框架使用了dart代码生成器,自动生成代码。这个数据库框架的优点是支持全平台使用,此框架通过使用datr语言的 ffi 功能(相当于JAVA的jni)来调用 sqlite 动态库 实现数据库操作,
Web平台是通过 wasm(浏览器可以运行的二进制文件,可由C、C++、rust、go 等语言编译而来)来调用sqlite, Web平台的sqlite数据库文件则通过 indexed db 虚拟文件系统保存。下面来介绍一下使用方法。

官方文档

https://drift.simonbinder.eu/docs/platforms/

先导入依赖

dependencies:
  drift: ^2.4.2
  sqlite3_flutter_libs: ^0.5.0
  #sqlcipher_flutter_libs: ^0.5.1
  path_provider: ^2.0.0
  path: ^1.8.3

dev_dependencies:
  drift_dev: ^2.4.1
  build_runner: ^2.3.3

这里是对每个包的作用的快速概述:

  • drift: 这是定义大多数 api 的核心包
  • sqlite3_flutter_libs: 提供 sqlite 动态库,如果要加密数据库,请使用 sqlcipher_flutter_libs
  • sqlcipher_flutter_libs: 提供 sqlcipher (加密版sqlite) 动态库,如果要加密数据库请添加此依赖,并移除sqlite3_flutter_libs依赖(共存会冲突)
  • path_provider 和 path:用于寻找合适的位置来存放数据库。 由 Flutter 和 Dart 团队维护
  • drift_dev:drift自动生成代码工具、 不会包含在最终应用程序中。
  • build_runner: 代码生成的通用工具,由 Dart 团队维护

下面仅示例使用加密版sqlite:
创建一个文件 databases.dart

import 'dart:io';

import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:logger/logger.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';

//引入自动生成的代码,刚开始会报错,运行 flutter pub run build_runner build 即可
part 'database.g.dart';

//数据库密码
const _encryptionPassword = 'password';

//定义一个表
class Notes extends Table {
  IntColumn get id => integer().autoIncrement()();

  TextColumn get content => text()();
}

//这个注解告诉 Drift 创建一个包含 我们上面定义的 Notes 表的数据库类(数据库操作类)
@DriftDatabase(tables: [Notes])
class MyEncryptedDatabase extends _$MyEncryptedDatabase {
  MyEncryptedDatabase() : super(_openDatabase());

  //设置数据库版本。
  @override
  int get schemaVersion => 1;

  //数据库迁移方法
  @override
  MigrationStrategy get migration {
    return MigrationStrategy(
      beforeOpen: (details) async {},
    );
  }
}

//自定义打开数据库的方法
QueryExecutor _openDatabase() {
  return LazyDatabase(() async {
    //通过路径提供器获取目录以存放数据库文件
    final path = (Platform.isMacOS || Platform.isIOS)
        ? await getApplicationDocumentsDirectory()
        : await getApplicationSupportDirectory();

    //这是数据库文件。
    final dbFile = p.join(path.path, 'databases', 'app.db');
    
    Logger().d(dbFile);
    return NativeDatabase(
      File(dbFile),
      setup: (db) {
        //这个语法检查数据库是否已经加密
        final result = db.select('pragma cipher_version');
        if (result.isEmpty) {
          throw UnsupportedError(
            'this database needs to run with sqlcipher, but that library is '
            'not available!',
          );
        }else{
          Logger().d("数据库已加密");
        }
        
        //数据库的密码。
        final escapedKey = _encryptionPassword;
        //通过密码打开数据库。
        db.execute("pragma key = '$escapedKey'");
      },
    );
  });
}

加密版本的数据库需要覆盖打开数据库的方法,创建一个 setup.dart

import 'package:sqlcipher_flutter_libs/sqlcipher_flutter_libs.dart';
import 'dart:ffi';
import 'package:sqlite3/open.dart';

setupDatabases() {
  open
    ..overrideFor(OperatingSystem.android, openCipherOnAndroid)
    ..overrideFor(OperatingSystem.iOS, DynamicLibrary.process);
    // 其他平台不需要覆盖
}

测试

运行生成代码命令

flutter pub run build_runner build

然后即可运行

Future<void> main() async {
  //确保组件树初始化
  WidgetsFlutterBinding.ensureInitialized();
  //覆盖数据库打开方法
  setupDatabases();
  //创建数据库
  final _database = MyEncryptedDatabase();
  //操作数据库
  _database.into(_database.notes).insert(NotesCompanion.insert(content: "test"));
  //查询
  var r = await _database.select(_database.notes).get();
  Logger().d(r);
  runApp(const MyApp());
  // runApp(const AutocompleteExampleApp());
}

Web 平台

这是native平台使用方法,
建议web平台另外创建一个分支,否则依赖会报错
web平台使用方法如下:
web平台的_openDatabase方法不同

import 'package:dio/dio.dart';
import 'package:drift/drift.dart';
import 'package:drift/wasm.dart';
import 'package:sqlite3/wasm.dart';
part 'database.g.dart';

// ...more code

QueryExecutor _openDatabase() {
  return LazyDatabase(() async {
    // 加载 sqlite3.wasm
    final response = await Dio().getUri(Uri.parse('sqlite3.wasm'),options: Options(
        responseType:ResponseType.bytes
    ));
    // 创建一个由 IndexedDb 支持的虚拟文件系统
    final fs = await IndexedDbFileSystem.open(dbName: 'my_app');
    final sqlite3 = await WasmSqlite3.load(
      response.data,
      SqliteEnvironment(fileSystem: fs),
    );

    // 然后,在该文件夹中打开一个数据库。
    return WasmDatabase(sqlite3: sqlite3, path: 'app.db');
  });
}

测试, web平台不需要调用 setupDatabases()

  1. 运行生成代码命令
flutter pub run build_runner build
  1. 把 sqlite3.wasm 放到web目录下, 必须使用定制的 sqlite3.wasm , 下载地址https://github.com/simolus3/sqlite3.dart/releases/tag/sqlite3-1.9.1

然后即可运行

Future<void> main() async {
  //确保组件树初始化
  WidgetsFlutterBinding.ensureInitialized();
  //创建数据库
  final _database = MyEncryptedDatabase();
  //操作数据库
  _database.into(_database.notes).insert(NotesCompanion.insert(content: "test"));
  //查询
  var r = await _database.select(_database.notes).get();
  Logger().d(r);
  runApp(const MyApp());
  // runApp(const AutocompleteExampleApp());
}
 类似资料: