Flutter数据库迁移插件sqflite_migration_service的使用

Flutter数据库迁移插件sqflite_migration_service的使用

在使用sqflite管理数据库时,通过user_version来跟踪数据库版本在调试部署和正式构建安装之间表现得非常不一致。
这个包是FilledStacks开发团队在生产环境中使用的,用于在更自动化的手动方式下管理SQFLite迁移。我知道这听起来有点奇怪,但你会明白我的意思。


设置

首先,你需要将包添加到你的pubspec.yaml文件中。

dependencies:
  ...
  sqflite_migration_service:

这个包专门与sqflite配合使用,建议你也使用它。如果你正在使用不同的SQLite数据库,请提交一个关于该库的问题,我可以抽象出数据库的使用方式并允许你提供自己的SQLite库。这个库为你提供了一个单一的服务:DatabaseMigrationService。你可以通过以下方式将其注册到injectable中:

locator.registerLazySingleton(() => DatabaseMigrationService());

或者,如果你使用的是Provider进行依赖注入,只需构造它并将其传递给Provider即可。


它的工作原理

制作这个包的主要目的是改进迁移代码的管理和可读性,并提供一种更清晰的方式来管理这些迁移。这个实现直接来自于我们的一位客户,他们为后端实现了这种迁移设置。使用这个库主要有三个部分:

  1. 使用特定命名创建SQL文件。
  2. DatabaseMigrationService提供这些SQL文件的列表。
  3. 在每次启动时运行迁移。

让我们从顶部开始。


创建SQL模式和文件

为了保持可读性,最佳方法是将文件分开保存,并使用适当的扩展名以允许代码高亮。在项目的根目录下创建一个名为assets的新文件夹。在该文件夹内创建一个名为sql的新文件夹。在该文件夹内创建一个名为1_create_schema.sql的新文件。这是命名约定:[schema_version_number]_[migration_name].sql

  • schema_version_number:任何数字,这将是与当前数据库版本进行比较的数字。如果此数字大于当前数据库版本,则会运行你在该SQL文件中编写的SQL查询,并在完成后更新数据库。
  • migration_name:我使用下划线分隔命名以保持与版本号的一致性,但你可以根据需要命名。这只是告诉你迁移的用途。保持简洁,但让名称有意义。我知道我们都不擅长命名,但坚持下去,让它直白一些。例如2_adds_description_into_todo.sql或如果是功能相关的3_alter_schema_for_favorites.sql。无论你如何命名,都可以随时更改,唯一不能改变的是前面的数字。你不希望相同的迁移再次运行。

文件中的每个查询必须用分号分隔。这是一个我们在项目中使用的示例SQL文件。

-- 支持SQL全行注释
CREATE TABLE user_address(
  id INTEGER PRIMARY KEY,
  customerId INT,  -- 也支持SQL行尾注释
  latitude FLOAT,
  longitude FLOAT,
  customerNotes TEXT,
  label TEXT,
  placeId TEXT,
  streetShort TEXT,
  streetAdditional TEXT,
  streetNumber TEXT,
  city TEXT,
  state TEXT,
  zip TEXT,
  current INT
);

CREATE TABLE cart_products(
  id INTEGER PRIMARY KEY,
  additional_instructions TEXT,
  description TEXT,
  name TEXT,
  item_token TEXT,
  menu_id INTEGER,
  price INTEGER,
  quantity INTEGER,
  vendor_id INTEGER,
  options TEXT
);

CREATE TABLE cart_information(
  cash_fee INTEGER,
  credit_fee INTEGER,
  service_fee INTEGER,
  tax INTEGER,
  product_ids TEXT,
  subtotal INTEGER
);

创建完该文件后,打开你的pubspec.yaml文件并在assets部分更新,添加整个assets/sql/文件夹。

assets:
  - assets/sql/

运行那些迁移

在你的DatabaseService的初始化函数中,现在可以调用runMigration,传入sqflitedatabase和所有迁移文件的列表。

await _migrationService.runMigration(
  _database,
  migrationFiles: [
    '1_create_schema.sql',
    '2_add_description.sql',
  ],
);

这将运行迁移1,如果数据库版本为1,则运行2。如果已经是1,则只会运行2。如果你不知道DatabaseService是什么,可以在FilledStacks的YouTube频道上观看有关SQLite的视频。就是这样。


添加新的迁移

这是添加新迁移的部分,当添加新迁移时,以下是步骤:

  1. sql文件夹中创建一个文件,确保版本号高于文件夹中的最后一个文件。
  2. 将文件名精确地添加到runMigration函数的migrationFiles列表中。

基本上就是这样。SQL格式的迁移文件,用分号分隔,并根据当前数据库的版本自动应用,且在部署过程中正确持久化。如果有任何问题,请不要犹豫提交。


自定义共享偏好中的数据库版本键

数据库版本号默认存储在共享偏好中,键名为database_version_key。可以通过在调用DatabaseServicerunMigration时传递参数databaseVersionKey来更改该键。

await _migrationService.runMigration(
  _database,
  migrationFiles: [
    '1_create_schema.sql',
    '2_add_description.sql',
  ],
  databaseVersionKey: 'custom_db_version_key'
);

示例代码

以下是一个完整的示例代码,展示如何使用sqflite_migration_service进行数据库迁移。

// main.dart
import 'package:flutter/material.dart';
import 'package:sqflite_migration_service/sqflite_migration_service.dart'; // 引入迁移服务
import 'package:sqflite/sqflite.dart'; // 引入sqflite
import 'package:get_it/get_it.dart'; // 用于依赖注入

final locator = GetIt.instance;

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // 注册依赖
  locator.registerLazySingleton(() => DatabaseMigrationService());

  // 初始化数据库迁移
  final database = await openDatabase('my_database.db');
  await locator.get<DatabaseMigrationService>().runMigration(
    database,
    migrationFiles: ['1_create_schema.sql', '2_add_description.sql'],
  );

  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: Scaffold(
        appBar: AppBar(title: Text('Sqflite Migration Example')),
        body: Center(child: Text('Migration Completed!')),
      ),
    );
  }
}

更多关于Flutter数据库迁移插件sqflite_migration_service的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter数据库迁移插件sqflite_migration_service的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


sqflite_migration_service 是一个用于 Flutter 的数据库迁移插件,它可以帮助你在应用升级时管理 SQLite 数据库的版本和迁移。通过这个插件,你可以轻松地定义和执行数据库迁移脚本,确保数据库结构与应用版本保持一致。

安装

首先,你需要在 pubspec.yaml 文件中添加 sqflite_migration_service 依赖:

dependencies:
  flutter:
    sdk: flutter
  sqflite: ^2.0.0+4
  sqflite_migration_service: ^1.0.0

然后运行 flutter pub get 来安装依赖。

使用步骤

  1. 创建迁移脚本

    你需要为每个数据库版本创建一个迁移脚本。迁移脚本通常是一个包含 SQL 语句的字符串列表。例如:

    final migration1to2 = [
      "ALTER TABLE users ADD COLUMN age INTEGER;",
    ];
    
    final migration2to3 = [
      "CREATE TABLE posts(id INTEGER PRIMARY KEY, title TEXT, content TEXT);",
    ];
    
  2. 配置迁移服务

    创建一个 MigrationConfig 对象,并将迁移脚本与对应的版本号关联起来:

    import 'package:sqflite_migration_service/sqflite_migration_service.dart';
    
    final migrationConfig = MigrationConfig(
      migrationScripts: {
        2: migration1to2,
        3: migration2to3,
      },
    );
    
  3. 初始化数据库

    使用 MigrationService 来初始化数据库,并传入 MigrationConfig

    import 'package:sqflite/sqflite.dart';
    import 'package:sqflite_migration_service/sqflite_migration_service.dart';
    
    Future<Database> initializeDatabase() async {
      final databasePath = await getDatabasesPath();
      final path = '$databasePath/my_database.db';
    
      final migrationService = MigrationService(
        config: migrationConfig,
      );
    
      return await migrationService.initializeDatabase(
        path: path,
        version: 3, // 当前数据库版本
        onCreate: (Database db, int version) async {
          // 在数据库首次创建时执行的代码
          await db.execute(
              "CREATE TABLE users(id INTEGER PRIMARY KEY, name TEXT);");
        },
      );
    }
    
  4. 使用数据库

    现在你可以像平常一样使用 sqflite 来操作数据库了:

    Future<void> insertUser(Database db) async {
      await db.insert('users', {'name': 'John Doe', 'age': 30});
    }
    
    Future<void> queryUsers(Database db) async {
      final List<Map<String, dynamic>> maps = await db.query('users');
      print(maps);
    }
    

完整示例

import 'package:flutter/material.dart';
import 'package:sqflite/sqflite.dart';
import 'package:sqflite_migration_service/sqflite_migration_service.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final db = await initializeDatabase();
  await insertUser(db);
  await queryUsers(db);
}

final migration1to2 = [
  "ALTER TABLE users ADD COLUMN age INTEGER;",
];

final migration2to3 = [
  "CREATE TABLE posts(id INTEGER PRIMARY KEY, title TEXT, content TEXT);",
];

final migrationConfig = MigrationConfig(
  migrationScripts: {
    2: migration1to2,
    3: migration2to3,
  },
);

Future<Database> initializeDatabase() async {
  final databasePath = await getDatabasesPath();
  final path = '$databasePath/my_database.db';

  final migrationService = MigrationService(
    config: migrationConfig,
  );

  return await migrationService.initializeDatabase(
    path: path,
    version: 3,
    onCreate: (Database db, int version) async {
      await db.execute(
          "CREATE TABLE users(id INTEGER PRIMARY KEY, name TEXT);");
    },
  );
}

Future<void> insertUser(Database db) async {
  await db.insert('users', {'name': 'John Doe', 'age': 30});
}

Future<void> queryUsers(Database db) async {
  final List<Map<String, dynamic>> maps = await db.query('users');
  print(maps);
}
回到顶部