数据库迁移

Finch 内置迁移系统,用于管理随时间推移的模式变更。

迁移命令

命令 描述
finch migrate --init 运行所有待执行的迁移
finch migrate --create --name <name> 创建新的迁移文件
finch migrate --rollback 回滚最后一次迁移
finch migrate --list 显示所有迁移的状态
finch migrate --init --sqlite 为 SQLite 运行
finch migrate --create --name <name> --sqlite 为 SQLite 创建

迁移文件结构

迁移文件位于 ./migrations/(MySQL)或 ./migrations_sqlite/(SQLite):

-- NEW VERSION
CREATE TABLE articles (
    id INT PRIMARY KEY AUTO_INCREMENT,
    title VARCHAR(255) NOT NULL,
    body TEXT,
    published TINYINT(1) DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- ROLL BACK
DROP TABLE IF EXISTS articles;

每个迁移文件必须包含 -- NEW VERSION 部分(用于应用)和 -- ROLL BACK 部分(用于回滚)。

每个迁移(基于文件或基于 Dart)都在数据库事务中执行:如果任何语句失败,事务将自动回滚,该迁移也不会被标记为已应用。

基于 Dart 的迁移

除了 SQL 文件,你还可以通过继承 DartMigration 用 Dart 定义迁移。使用 addSql() 构建 up()/down() 的 SQL,并设置 target 来选择 MigrationTarget.mysqlMigrationTarget.sqlite:

import 'package:finch/mysql.dart';

class M1CreateUsers extends DartMigration {
  @override
  MigrationTarget get target => MigrationTarget.sqlite;

  M1CreateUsers() : super('m1_create_users');

  @override
  void up() {
    addSql('''
      CREATE TABLE users (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT,
        email TEXT
      );
    ''');
  }

  @override
  void down() {
    addSql('DROP TABLE IF EXISTS users;');
  }
}

FinchApp 实例上注册迁移。传给 super(...) 的名称在所有已注册的迁移中必须是唯一的:

final app = FinchApp(configs: configs)
  ..registerDartMigration([
    M1CreateUsers(),
    M2InsertUsers(),
  ]);

注册后,migrate --init--rollback--list(及其 --sqlite / migrate_sqlite 对应命令)会运行匹配该 target 的 Dart 迁移,而不是读取迁移文件。

文件命名

迁移文件以时间戳命名:

2024_01_15_143022_create_articles_table.sql

启动时自动运行

你可以在应用启动时自动运行迁移:

FinchConfigs(
  autoMigrate: true,
)

警告:在生产环境中谨慎使用此选项。

示例工作流

# 为新表创建迁移
finch migrate --create --name create_products_table

# 编辑 migrations/ 中生成的文件
# 然后运行迁移
finch migrate --init

# 如需回滚
finch migrate --rollback