drift調べる
公式サイト
pubspec.yaml
dependencies:
drift: ^2.16.0
sqlite3_flutter_libs: ^0.5.0
path_provider: ^2.0.0
path: ^1.9.0
dev_dependencies:
drift_dev: ^2.16.0
build_runner: ^2.4.8
または
dart pub add drift sqlite3_flutter_libs path_provider path dev:drift_dev dev:build_runner
lib/database.dart
import 'package:drift/drift.dart';
part 'database.g.dart';
class TodoItems extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get title => text().withLength(min: 6, max: 32)();
TextColumn get content => text().named('body')();
IntColumn get category => integer().nullable()();
}
(tables: [TodoItems])
class AppDatabase extends _$AppDatabase {
}
dart run build_runner watch
database.dart
import 'dart:io';
import 'package:drift/native.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as p;
import 'package:sqlite3/sqlite3.dart';
import 'package:sqlite3_flutter_libs/sqlite3_flutter_libs.dart';
//...
(tables: [TodoItems])
class AppDatabase extends _$AppDatabase {
AppDatabase() : super(_openConnection()); // 追加
int get schemaVersion => 1; // 追加
}
// 追加
LazyDatabase _openConnection() {
return LazyDatabase(() async {
final dbFolder = await getApplicationDocumentsDirectory();
final file = File(p.join(dbFolder.path, 'db.sqlite'));
if (Platform.isAndroid) {
await applyWorkaroundToOpenSqlite3OnOldAndroidVersions();
}
final cachebase = (await getTemporaryDirectory()).path;
sqlite3.tempDirectory = cachebase;
return NativeDatabase.createInBackground(file);
});
}
driftメモ
概要
DriftはDartやFlutterアプリのためのリレーショナル永続化ライブラリです。
DartやSQLで型安全なクエリを書いたり、自動更新のストリームを楽しんだり、簡単に管理できるトランザクションなど、永続化を楽しくする様々なことができます。
宣言的なテーブル、流暢なクエリ
driftを使用すると、高度なSQL機能を使用することなく、純粋なDartでデータベースのテーブルとクエリを宣言することができます。
テーブルの作成はDriftが行い、データに対して流暢なクエリを実行するためのコードを生成します。
間違いなくリレーショナル
Driftは、SQLを隠そうとして、最初の集約や明白でない結合で破綻するようなORMではない。
その代わり、Driftはリレーショナルデータベースを採用し、SQLに近いながらも習得しやすいDart APIを提供している。高度な式やサブクエリもすぐにサポートされる。
ORMとは
ORM(Object-Relational Mapping)は、データベース内のデータとオブジェクトを関連付けるためのツールです。
これは、データベース内のテーブルと、プログラミング言語でのクラスやオブジェクトの間のマッピングを容易にします。
通常、データベース内のテーブルは、行と列で構成されていますが、オブジェクト指向プログラミングでは、データをプロパティとメソッドを持つオブジェクトとして扱います。ORMは、これらの異なるデータモデル間の変換を処理し、データベース操作をオブジェクト指向プログラミングの文脈で行うことを可能にします。
具体的には、ORMは、データベースからデータを取得し、それをオブジェクトにマッピングすることで、データの抽象化と簡素化を提供します。また、オブジェクトをデータベースに保存する際にも、ORMは適切なSQLクエリを生成してデータベースに保存します。
ORMを使用することで、プログラマーはデータベースとのやり取りに関連する複雑さを隠蔽し、オブジェクト指向プログラミングの利点を享受できます。
安全なスキーマ
適切に選択されたSQLスキーマは、型安全なクエリを可能にし、見つけにくいミスを回避します。driftはスキーマの移行を幅広くサポートしているため、スキーマの変更は安全かつ簡単に行うことができます。さらに、drift は完全なテストツールキットを提供し、すべてのリビジョン間の移行をテストすることができます。
スキーマ
データベースにおける「スキーマ(Schema)」は、データベース内で使用されるデータ構造を定義する仕組みです。スキーマは、データベース内のテーブル、列、制約、インデックスなどの構造を定義します。具体的には、以下のような要素を含むことがあります。
-
テーブル: データの保存に使用される構造化されたデータのセット。テーブルは、行と列で構成され、列はデータの型と制約を持ちます。
-
列(カラム): テーブル内のデータ要素を識別するためのフィールド。列にはデータ型や制約(NULL許容、一意性、デフォルト値など)が指定されます。
-
制約(Constraint): データの整合性を保つためのルール。主キーや外部キー制約、一意性制約、NOT NULL制約などがあります。
-
インデックス(Index): データベースのパフォーマンスを向上させるための機能で、特定の列に対する高速な検索を可能にします。
-
ビュー(View): 1つ以上のテーブルから抽出された、仮想的な表現です。ビューはクエリの結果として扱われ、実際のデータを持ちません。
-
ストアドプロシージャ(Stored Procedure): データベース内で事前に定義された手続きや関数であり、データベースの操作を実行するために使用されます。
スキーマは、データベース管理システム(DBMS)によって管理され、データベースの構造や関連する制約などを定義するために使用されます。スキーマの変更はデータベースの構造自体を変更することを意味し、慎重に行う必要があります。
型安全
データベースにおける「型安全(Type Safety)」は、プログラムやクエリがデータベースにアクセスする際に、データ型の整合性を保証することを指します。具体的には、以下のような側面が含まれます。
-
静的型付け: データベースへのクエリやデータ操作が行われる際に、コンパイル時にデータ型の整合性が確認されます。これにより、実行時にデータ型の不整合によるエラーや予期せぬ結果を防ぐことができます。
-
データ型の明示性: データベーススキーマやORM(Object-Relational Mapping)によって、テーブルや列のデータ型が明確に定義されます。これにより、データベース内のデータが適切な型で保存され、正確なデータ操作が行われることが保証されます。
-
エラーの早期発見: プログラムやクエリが実行される前に、データ型の整合性が確認されるため、データベースに不正なデータが挿入される可能性が低くなります。これにより、エラーが早期に発見され、修正が容易になります。
-
安全性の向上: 型安全なデータベース操作は、データベース内のデータの整合性を維持し、データの品質を向上させます。これにより、システム全体の安全性が向上し、バグやセキュリティ上のリスクを軽減することができます。
型安全性は、特に大規模なアプリケーションやデータベースを管理する場合に重要であり、データの信頼性と品質を確保するための基本的な概念です。
SQLがお好きですか?Driftなら大丈夫です!
Driftには強力なSQLパーサーとアナライザーが搭載されており、すべてのSQLクエリに対して型安全なメソッドを作成することができます。すべてのSQLクエリはビルド時に検証・分析されるため、Driftは潜在的なエラーに関するヒントを素早く提供し、効率的なマッピングコードを生成することができます。もちろん、SQLとDartをお好みで混ぜることもできます。
お気に入りのプラットフォームでサポート
DriftのコアAPIは、バックエンドとして様々なデータベースライブラリをサポートするように書かれており、Flutterを必要としません。DriftはAndroid、iOS、macOS、Linux、Windows、そしてウェブで第一級のサポートを提供しています。
他のデータベースライブラリも簡単にdriftに統合することができる。
setup
依存関係
dependencies:
drift: ^2.16.0
sqlite3_flutter_libs: ^0.5.0
path_provider: ^2.0.0
path: ^1.9.0
dev_dependencies:
drift_dev: ^2.16.0
build_runner: ^2.4.8
または
dart pub add drift sqlite3_flutter_libs path_provider path dev:drift_dev dev:build_runner
-
drift: データベースにアクセスするためのAPIを定義するコアパッケージ。
-
sqlite3_flutter_libs: AndroidやiOSアプリに最新のsqlite3バージョンを提供。Flutterを使用していない場合は不要だが、その場合は自分でsqlite3を含める必要がある。このパッケージは、armv8、armv7、x86、x86_64のアーキテクチャ向けのネイティブsqlite3ライブラリが含まれる。ほとんどのFlutterアプリは32ビットx86デバイスで実行されないため、x86ビルドが不要な場合はbuild.gradleにスニペットを追加する必要がある。そうしないと、Playストアがサポートされていないx86デバイスのユーザーにアプリをインストールさせる可能性がある。現在のFlutterのネイティブビルドシステムでは、driftはそれを代わりに行うことができない。
-
path_provider と path: データベースを保存する適切な場所を見つけるために使用される。FlutterおよびDartチームによってメンテナンスされている。
-
drift_dev: 開発時のみに使用される依存関係。テーブルに基づいたクエリコードを生成する。最終的なアプリには含まれない。
-
build_runner: コード生成のための共通ツール。Dartチームによってメンテナンスされている。
データベースクラス
driftを使用するすべてのプロジェクトには、データベースにアクセスするためのクラスが少なくとも1つ必要。
このクラスは使用するすべてのテーブルを参照し、driftのコードジェネレータの中心的なエントリポイントになります。
データベースクラスには@DriftDatabse
というアノテーションをつける。
tables
に使用するテーブルが記載される。
build_runner
はこのアノテーションを目印に、ファイルを生成する。これはORMライブラリでは一般的な手法。
(tables: [TodoItems])
class AppDatabase extends _$AppDatabase {}
テーブルを作成するにはTableクラスを継承する必要がある。
class SomeData extends Table {...}
- Dartでテーブルを定義する方法: https://drift.simonbinder.eu/docs/getting-started/advanced_dart_tables/
- サポートしているカラムの型: https://drift.simonbinder.eu/docs/getting-started/advanced_dart_tables/#supported-column-types
- カラムのデータ型: https://drift.simonbinder.eu/api/drift/drift-library.html#typedefs
データベースクラスが記載されたファイルにはpart 'database.g.dart';
が存在する必要がある。
最初はエラーが出るが、これはbuild_runnerを実行した際に解決される。
-
dart run build_runner build
: 必要なコードをすべて一度に生成します。 -
dart run build_runner watch
: ソースの変更を監視し、インクリメンタルリビルドでコードを生成します。これは開発セッションに適しています。
この時点ではまだエラーが表示される。
AppDatabaseのコンストラクタにdriftでデータベースを開く方法を定義する。
schemaVersionのゲッターにはデータベースのスキーマが変更された際に更新するバージョン値を記載する。
データベースクラスのコンストラクタ
データベース起動時の処理は、データベースクラスのコンストラクタに定義される。
ドキュメントではLazyDatabase
を使用している。これは遅延初期化と言って、データベースが必要になるまでデータベース接続を生成しないというもの。必要になったら初めて接続が生成されるので、パフォーマンスの向上やリソースの節約、柔軟性の向上が期待できる。
main.dart
import 'package:drift_sample/database.dart';
import 'package:flutter/widgets.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final database = AppDatabase();
await database.into(database.todoItems).insert(TodoItemsCompanion.insert(
title: 'todo: finish drift setup',
content: 'We can now write queries and define our own tables.',
));
List<TodoItem> allItems = await database.select(database.todoItems).get();
print('items in database: $allItems');
}
database.dart
// These imports are necessary to open the sqlite3 database
import 'dart:io';
import 'package:drift/native.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as p;
import 'package:sqlite3/sqlite3.dart';
import 'package:sqlite3_flutter_libs/sqlite3_flutter_libs.dart';
import 'package:drift/drift.dart';
part 'database.g.dart';
// テーブル定義
class TodoItems extends Table {
// カラム定義
IntColumn get id => integer().autoIncrement()();
TextColumn get title => text().withLength(min: 6, max: 32)();
TextColumn get content => text().named('body')();
IntColumn get category => integer().nullable()();
}
// データベース定義
@DriftDatabase(tables: [TodoItems])
class AppDatabase extends _$AppDatabase {
// 起動時の処理を指定
AppDatabase() : super(_openConnection());
@override
int get schemaVersion => 1;
}
// データベース起動時の処理定義
LazyDatabase _openConnection() {
return LazyDatabase(() async {
// データベースのデータを保存するファイル指定
final dbFolder = await getApplicationDocumentsDirectory();
final file = File(p.join(
dbFolder.path,
'my_db.sqlite',
));
// sqlite3をandroidに導入する際の対策
if (Platform.isAndroid) {
await applyWorkaroundToOpenSqlite3OnOldAndroidVersions();
}
final cachebase = (await getTemporaryDirectory()).path;
sqlite3.tempDirectory = cachebase;
return NativeDatabase.createInBackground(file);
});
}