SQLite
Finch gebruikt het sqlite3-pakket voor SQLite. SQLite is een bestandsgebaseerde database — er is geen aparte databaseserver om te beheren. Alle gegevens worden opgeslagen in één enkel bestand op schijf.
SQLite is een goede keuze wanneer:
- Je een kleine of middelgrote applicatie bouwt die geen hoge mate van concurrency nodig heeft
- Je een deployment zonder externe afhankelijkheden wilt (geen externe databaseserver nodig)
- Je lokaal ontwikkelt en een snelle opzet wilt
De SQLite-integratie in Finch gebruikt dezelfde MTable-, MField*- en Sqler-API als MySQL (zie MySQL voor de volledige referentie van schema en querybouwer) — dezelfde Sqler-aanroep produceert geldige SQL voor welke database DatabaseDriver ook daadwerkelijk vasthoudt, omdat DatabaseDriver tijdens runtime het onderliggende verbindingstype detecteert. De verschillen zitten in: de configuratie, de eigenschap waarmee je bij de driver kunt, en — belangrijker nog — hoe je waarden weer uitleest uit een resultset (zie Result Handling hieronder).
Configuratie
Voeg sqliteConfig toe aan FinchConfigs. De enige verplichte parameter is het pad naar het .sqlite-bestand:
FinchConfigs configs = FinchConfigs(
sqliteConfig: FinchSqliteConfig(
enable: true,
filePath: env.get('SQLITE_PATH', './app.sqlite'),
),
);
Het bestand wordt automatisch aangemaakt als het nog niet bestaat. Het pad kan absoluut zijn of relatief ten opzichte van de werkmap.
Toegang tot de driver
// DatabaseDriver<Database> — gebruik dit voor Sqler-queries
var driver = app.sqliteDriver;
// Directe sqlite3.Database — gebruik dit alleen wanneer je low-level toegang nodig hebt
var db = app.sqliteDb;
Gebruik app.sqliteDriver in je datalaag-klassen. Geef deze door als constructorargument om controllers dun te houden:
class BooksData {
final DatabaseDriver db;
BooksData(this.db);
// je querymethoden...
}
// In een controller:
var books = BooksData(app.sqliteDriver);
Een tabel definiëren
Tabelschema's worden op identieke wijze gedefinieerd als bij MySQL, met MTable en MField*. Finch importeert beide vanuit finch_mysql.dart — ze worden gedeeld tussen MySQL en SQLite:
import 'package:finch/finch_mysql.dart'; // MTable, MField* worden gedeeld door MySQL en SQLite
import 'package:finch/finch_ui.dart'; // FieldValidator
final table = MTable(
name: 'books',
fields: [
MFieldInt(name: 'id', isPrimaryKey: true, isAutoIncrement: true, isNullable: false),
MFieldVarchar(
name: 'title',
isNullable: false,
validators: [
FieldValidator.requiredField().toSimple(),
FieldValidator.fieldLength(min: 3, max: 255).toSimple(),
],
),
MFieldVarchar(name: 'author', isNullable: false),
MFieldDate(name: 'published_date', isNullable: false),
MFieldInt(name: 'category_id', isNullable: true),
],
);
MFieldInt wordt automatisch gemapt naar SQLite's INTEGER wanneer de SQL wordt gegenereerd voor een SQLite-verbinding (toSQL<Sqlite>() versus toSQL<Mysql>()) — je hebt geen SQLite-specifiek veldtype nodig. Zie MySQL — Available Field Types voor de volledige lijst, inclusief MFieldBoolean (niet MFieldBool) en MFieldDecimal/MFieldFloat (er bestaat geen MFieldDouble).
Queries met Sqler
Queries worden opgebouwd met dezelfde fluent Sqler-API als bij MySQL. Finch handelt de verschillen in SQL-dialect intern af:
import 'package:finch/finch_mysql.dart';
Future<SqlDatabaseResult> getAllBooks(DatabaseDriver db) async {
var query = Sqler()
..from(QField(table.name, as: 'b'))
..selects([
QSelect('b.id'),
QSelect('b.title'),
QSelect('b.author'),
])
..orderBy(QOrder('b.id', desc: true))
..limit(20);
return db.execute(query);
}
Invoegen
Sqler.insert() neemt de doeltabel en een lijst van rij-maps:
await db.execute(
Sqler().insert(QField(table.name), [
{'title': QVar('Dart in Action'), 'author': QVar('Alice')},
]),
);
Bijwerken
Gebruik .update(table), vervolgens .updateSet(field, value) per veld, en daarna .where():
await db.execute(
Sqler()
..update(QField(table.name))
..updateSet('title', QVar('Updated Title'))
..where(WhereOne(QField('id'), QO.EQ, QVar(1))),
);
Verwijderen
Combineer .delete() met .from():
await db.execute(
Sqler()
..delete()
..from(QField(table.name))
..where(WhereOne(QField('id'), QO.EQ, QVar(1))),
);
Dezelfde MTable-hulpmethoden en de repositorybasisklasse SqliteTable (die MysqlTable weerspiegelt, met deleteBy/deleteById/findById/countBy) zijn hier ook beschikbaar — zie MySQL — Table Convenience Methods voor de volledige methodenlijst; alles daar werkt ongewijzigd tegen app.sqliteDriver.
Result Handling
Dit is de ene plek waar SQLite echt verschilt van MySQL: een SQLite SqlDatabaseResult.rows is een gewone List<List<Object?>> (positionele waarden per rij) — geen objecten met een .colByName()-methode zoals de resultaatrijen van MySQL die hebben. Gebruik altijd .assoc/.assocFirst om kolommen op naam te lezen in plaats van op positie te indexeren in .rows:
var result = await getAllBooks(app.sqliteDriver);
for (var row in result.assoc) {
var title = row['title']; // Map<String, String?> — elke waarde komt terug als een String
}
var firstRow = result.assocFirst; // Map<String, String?>? — eerste rij of null
var numRows = result.numRows;
var newId = result.insertId; // laatste insert row id van sqlite3
var affected = result.affectedRows; // rijen gewijzigd door de laatste instructie
Let op: in tegenstelling tot MySQL's
.assoc(dat de native Dart-typen behoudt), zetten SQLite's.assoc/.assocFirstelke waarde om naar een string (row[i]?.toString()). Parse getallen/booleans expliciet terug als je ze getypeerd nodig hebt (bijv.int.parse(row['id']!)).
Voor een pagineringsquery met een totaal-rijenaantal alias je COUNT(*) als count_records en lees je .countRecords, precies zoals bij MySQL:
var countQuery = Sqler()..from(table.qName)..addSelect(SQL.count(QField('id', as: 'count_records')));
var total = (await table.execute(driver, countQuery)).countRecords;
Migraties
SQLite-migraties worden apart bijgehouden van MySQL-migraties, met hun eigen dedicated app-runtime commando, migrate_sqlite:
# Alle openstaande SQLite-migraties toepassen
finch run --args="migrate_sqlite --init"
# Nieuw SQLite-migratiebestand aanmaken
finch migrate --create --name add_books_table --sqlite
finch migrate --init --sqlite(het gecombineerde MySQL-commando met een--sqlite-vlag) past ook openstaande SQLite-migraties toe, in dezelfde aanroep als die van MySQL — maar alleen voor--init.migrate --rollback --sqliteenmigrate --list --sqlitenegeren de--sqlite-vlag stilzwijgend en werken alleen op MySQL; gebruik hiervoormigrate_sqlite --rollback/migrate_sqlite --list. Zie Database Migration voor de volledige commandoreferentie en het migratiebestandsformaat.
SQLite is ideaal voor ontwikkeling, testen en kleine productie-implementaties waarvoor geen aparte databaseserver nodig is.