Databasemigratie
Databasemigraties zijn geversioneerde scripts die wijzigingen in je databaseschema in de loop van de tijd bijhouden. In plaats van handmatig SQL-commando's op elke omgeving uit te voeren, zorgen migraties ervoor dat elke database — development, staging, productie — automatisch synchroon blijft.
Finch houdt toegepaste migraties bij in een wa_migration-tabel die het automatisch aanmaakt (per database), en biedt twee gerelateerde maar verschillende app-runtime commando's: migrate voor MySQL, en migrate_sqlite voor SQLite. Beide zijn app-runtime commando's — ze draaien binnen je daadwerkelijke FinchApp-instantie, niet als een kale top-level finch-vlag. Zie Finch CLI voor hoe de twee commandolagen zich tot elkaar verhouden.
Migratie-commando's
Voer deze uit via finch run --args="..." / finch serve --args="...", of rechtstreeks met dart run lib/app.dart ...:
| Commando | Optie | Kort | Beschrijving |
|---|---|---|---|
migrate |
--init |
-i |
Alle openstaande MySQL-migraties in volgorde toepassen |
migrate |
--init --sqlite |
Openstaande MySQL- en SQLite-migraties samen toepassen, in één aanroep | |
migrate |
--rollback |
-r |
De laatst toegepaste MySQL-migratie terugdraaien |
migrate |
--list |
-l |
MySQL-migratiebestanden en hun toepassingsstatus weergeven |
migrate_sqlite |
--init / --rollback / --list |
-i / -r / -l |
Dezelfde bewerkingen, beperkt tot alleen SQLite |
Belangrijk:
--sqliteop hetmigrate-commando heeft alleen effect op--init— het voert dan als gemak beide databases' openstaande migraties samen uit. Het wordt genegeerd door--rollbacken--list;migrate --rollback --sqlitedraait nog altijd een MySQL-migratie terug. Gebruik voor het terugdraaien of weergeven van SQLite-migraties altijd het specifiekemigrate_sqlite-commando.
Het aanmaken van een nieuw migratiebestand (in tegenstelling tot het toepassen ervan) is juist een top-level finch CLI-commando — zie Creating a Migration File hieronder.
Eerste installatie
Voer --init uit om alle migraties toe te passen op een nieuwe database:
# Alleen MySQL
finch run --args="migrate --init"
# MySQL en SQLite samen
finch run --args="migrate --init --and migrate_sqlite --init"
# of, equivalent, in één migrate-aanroep:
finch run --args="migrate --init --sqlite"
# Alleen SQLite
finch run --args="migrate_sqlite --init"
Finch maakt bij de eerste uitvoering een wa_migration-tabel aan (per database) om bij te houden welke bestanden al zijn uitgevoerd. Volgende aanroepen van --init passen alleen de bestanden toe die nog niet zijn uitgevoerd.
Creating a Migration File
Het genereren van een nieuw migratiebestand is een top-level finch CLI-commando (de app hoeft hiervoor niet te draaien, aangezien het slechts een bestand opzet):
finch migrate --create --name add_books_table
# Maakt iets aan als: migrations/z1700000000000_add_books_table_migration.sql
finch migrate --create --name add_books_table --sqlite
# Maakt het SQLite-equivalent aan onder pathMigrationSQLite
De bestandsnaam krijgt het voorvoegsel z<millisecondsSinceEpoch>_ (de voorloop-z zorgt ervoor dat bestanden met een tijdstempel-voorvoegsel na eventuele niet-migratiebestanden gesorteerd blijven) en slugifieert de naam die je hebt opgegeven. Het bestand wordt geplaatst in de map pathMigrationMySQL (of pathMigrationSQLite voor SQLite), beide geconfigureerd in FinchConfigs:
FinchConfigs configs = FinchConfigs(
pathMigrationMySQL: pathTo('./migrations'),
pathMigrationSQLite: pathTo('./migrations_sqlite'),
);
Of
--createeen.sql-bestand genereert of kant-en-klare boilerplate voor een DartDartMigration-klasse, wordt bepaald door de instellingmysql_migrate.type/sqlite_migrate.typein jepubspec.yaml(standaardsql) — zie pubspec.yaml Configuration.
Migratiebestandsstructuur
Elk .sql-migratiebestand bestaat uit twee secties: de voorwaartse migratie (## NEW VERSION) en de terugdraaisectie (## ROLL BACK):
-- 2024-01-15 12:00:00.000
-- MySQL Migration File
-- Name: add_books_table
-- ## NEW VERSION:
CREATE TABLE IF NOT EXISTS `books` (
`id` INT NOT NULL AUTO_INCREMENT,
`title` VARCHAR(255) NOT NULL,
`author` VARCHAR(255) NOT NULL,
`published_date` DATE NOT NULL,
`category_id` INT NULL,
PRIMARY KEY (`id`)
);
-- ## ROLL BACK:
DROP TABLE IF EXISTS `books`;
Elke migratie (bestands- of Dart-gebaseerd) draait binnen een databasetransactie: als een instructie mislukt, wordt de transactie automatisch teruggedraaid en wordt de migratie niet vastgelegd in wa_migration, zodat een gecorrigeerde versie van hetzelfde bestand wordt opgepikt door de volgende --init.
Dart-gebaseerde migraties
In plaats van SQL-bestanden kun je migraties in Dart definiëren door DartMigration te extenden. Bouw de SQL voor up()/down() op met addSql(), en stel target in om MigrationTarget.mysql of MigrationTarget.sqlite te kiezen:
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;');
}
}
Registreer migraties op de FinchApp-instantie. De naam die aan super(...) wordt doorgegeven, moet uniek zijn onder alle geregistreerde migraties — dit is wat wordt vastgelegd in wa_migration, dus als je deze later hernoemt, denkt Finch dat de migratie nooit is toegepast:
final app = FinchApp(configs: configs)
..registerDartMigration([
M1CreateUsers(),
M2InsertUsers(),
]);
Eenmaal geregistreerd, voeren migrate --init/--rollback/--list en hun migrate_sqlite-tegenhangers de Dart-migraties uit die overeenkomen met dat target, naast eventuele .sql-bestanden in de migratiesmap — je kunt beide stijlen vrij combineren binnen hetzelfde project.
Terugdraaien
# De laatst toegepaste MySQL-migratie ongedaan maken
finch run --args="migrate --rollback"
# De laatst toegepaste SQLite-migratie ongedaan maken — hiervoor moet je migrate_sqlite gebruiken
finch run --args="migrate_sqlite --rollback"
# De twee meest recente MySQL-migraties ongedaan maken
finch run --args="migrate --rollback 2"
Het terugdraaien voert de SQL uit de ## ROLL BACK:-sectie van het laatst toegepaste bestand uit (of de down()-methode van de laatst toegepaste Dart-migratie).
Migratiestatus weergeven
finch run --args="migrate --list" # MySQL
finch run --args="migrate_sqlite --list" # SQLite
Dit print een tabel met elk migratiebestand, of het is uitgevoerd, en wanneer.
Migraties uitvoeren bij opstarten
Voer migraties automatisch uit als onderdeel van je opstartcommando — dit is precies wat de Docker-image van het voorbeeldproject doet (zie Docker for Finch):
# Bij het opstarten in productie (bijv. Docker CMD)
finch serve --args="migrate --init --and migrate_sqlite --init"
--and koppelt meerdere app-runtime commando's aan elkaar in één --args-string, zodat beide databases gemigreerd zijn voordat de server verzoeken begint te verwerken.
Maak altijd een back-up van je productiedatabase voordat je migraties uitvoert.