この記事の続きです
まずはDriftの話
Driftの記事って全部古いと思うんです.
なんか公式のチュートリアルと違うやんけってことすっごく多くないですか?
というわけで公式チュートリアル,とFlutterの世界で有名な人との比較をしましょう.
公式チュートリアル
Flutter 大学のDriftに関する記事
なんなら,初っ端らから依存環境が違います.
dependencies:
drift: ^1.5.0
path: ^1.8.0
path_provider: ^2.0.9
sqlite3_flutter_libs: ^0.5.5
dev_dependencies:
build_runner: ^2.1.8
drift_dev: ^1.5.2
dependencies:
drift: ^2.33.0
drift_flutter: ^0.3.1-wip
path_provider: ^2.1.5
dev_dependencies:
drift_dev: ^2.33.0
build_runner: ^2.15.0
正直,今のOSSのバージョンに方には,破壊的変更も入っているので,一概に言えませんが,なんだかあまりにも違いますよね.
なんなら,sqlite3_flutter_libsは今のDriftでは非推奨パッケージになっています.
代わりに,drift_flutterというパッケージが,基本的な代替パッケージになっているんです.
ただし,代替パッケージになているからって役割が思いっきり変わっているかというとそうではなく,sqliteを使うためのパッケージである点は変わりません.
(なぜなら,drift_postgreというパッケージも存在するため...)
他にも,初期化コードなど大きく変わったところもありますし,マイグレにはstepbystepが推奨されているなど色々書くべきなことはありますが,まずは使うためのワークフローを確認していきます.
Driftを使うためのワークフロー
理解しとくべき固有表現は次のとおりです.
テーブル定義
これは,DriftのTableクラスを使って,定義される,テーブルの構造のことを指します
Driftのデータベース
データベースと言っても,Driftが管理するデータベース仮想的なとSQLiteなどのデータベースの実態があります.
Driftのデータベースは,SQLiteなどの実態に対して,操作するための仮想的なデータベースです.
Driftはこの,抽象的なデータベースを持ち,このデータベースに対する変更を実態に反映する作業を代行してくれるため,データベースがDartのライフサイクルの中で扱いやすくなるのです.
これが,いわゆるORMのキモです.
スキーマ
さっきの二つの意味を合わせたもので,データベース全体の構造のことです.
テーブル定義も,データベースの構造も,参照も,どんなルールもすべてスキーマと呼ばれます.
要は,このスキーマさえあれば,実際がなくても,どんなデータベースかはわかるということです.
このスキーマの違いをバージョンごとに分けて,それぞれ段階的に,新しくしていう作業を,マイグレーションと言いいます.
1. Drift関連パッケージをインストールする
これは,いつも通りDependenciesをpubspec.yamlに書くだけですね.
その後に
flutter pub get
をするだけです.
2. テーブル定義を書く
これは,package:drift/drift.dartに含まれる,Tableクラスを継承したクラスを作ることで解決します.
よく出てくる,こういうコードのことです.
要はテーブル定義を書くためのコードですね.
チュートリアルなどでは,データベース定義ファイルを同義で書かれることが多いですが,当然ただのクラスなので,ファイル分割しても問題ありません!
(なんならファイル分割しないと,テーブル定義が大きくなりすぎて,コードを追えなくなります!!)
3. テーブル定義を,Driftのデータベースに登録する
Driftのデータベースに登録するためのコードは簡単です.
import 'package:drift/drift.dart'
import '{自分で作ったデータベース定義のファイル}.dart'
part 'database.g.dart'
@DriftDatabase(tables:[DiaryItems] // 筆者が作ったDiaryItemsっていうテーブルを登録してる
class Database extends _$Database {}
この状態で,以下のコードを実行しましょう.
flutter pub run build_runner watch --delete-conflicting-outputs -v
これで,登録完了です!
賢い方なら,「あれ?_openConnectionとかの文字が見えないぞ?」と気付いたかもしれません.
大丈夫です.登録だけなら,上のコードだけで大丈夫です.
「データベース実態への接続,データベース実態の作成」という手段(method)が必要になってから,そのコードは書くんです.
このdartファイルが,データベース実体を扱うためのエントリーポイントになります.
4. データベースの初回作成,接続コードの作成
ではさっきのコードに,接続するためのコードを書き足していきましょう.
import 'package:drift/drift.dart'
import 'package:path/path.dart'
import 'package:path_provider/path_provider.dart'
part 'database.g.dart';
@DriftDatabase(tables: [DiaryItems])
class Database extends _$Database {
Database() : super(_openConnection());
@override
int get schemaVersion => 1;
static LazyDatabase _openConnection() {
return LazyDatabase(() async {
final dbFolder = await getApplicationSupportDirectory();
final file = File(p.join(dbFolder.path, 'db.sqlite'));
return NativeDatabase.createInBackground(file, logStatements: true);
});
}
まずは,ちょっと離れた説明をします.
データベースを作成するには,接続するには,Queryが必要です.
このDriftにはQueryを簡単にしてくれる,QueryExecutorというリポジトリがあります.
このリポジトリの具体実装の一つがLazyDatabaseというものです.
LazyDatabaseでは,Databaseを接続,または作成を行ってくれます.
ですが,チュートリアルではdriftDatabaseという関数が使われています.
この2つの差は,同じ処理を非同期でするのか,同期でするのかという違いだけです.
Lazyは怠けているという意味ですが,そのまんま,File名の取得など重たい時間のかかるI/O処理などが終わってから,実際にクエリーを発行するのです.
コードに戻ります.
_openConnection()は,そんなLazyDatabaseを返すだけの関数です.
いつでも実行できるように,staticをつけておいてください.
ちなみに,schemaVersionというのは,その名の通りで,作成されるDriftのデータベースの構造のバージョンを表します.
実体のデータベースにも,ユーザー定義バージョンを保存する場所があり,そこの値と比較することで,バージョンに違いがあるかを判定し,マイグレーションの可否を判定します.
5. あとは好きなCRUD処理を,databaseコードに記入する
文字のとおりです.
最終的な,コードを記載しますね.
特徴的なもので言うと,Stream<List<DiaryItem>>を返す関数ですね.
これは,Driftに備えられたWatchの機能をバイパスするための関数です.
実際は,指定されたデータベースのエリアの変更を感知して,その変更を伝えると言うものです.
面白いのが,これは, Selectクエリに結合できると言うことです.
要は,このコードみたいに,データベース全体の変更通知を扱うこともできるし,SelectクエリにWhereクエリを結合して,特定の行のみが更新されたら変更通知を出すことだってできます.
これが,他のORMに比べて扱いやすいと言われている所以ですね.
sqfliteを見てきた層なら,結構驚きだと思います.
Riverpodなどと組み合わせると,クッソ使いやすいStreamが出来上がりますよ!
おわりに
簡単に,Driftの使い方を,チュートリアル形式と,実際のコードを説明する形で,お伝えできたでしょうか.
できていないと思いますが...
なんか日本語が思い浮かんでこなかったので...
次回以降は,アプリ全体のアーキテクチャの話をして,この実践編を終わらせようと思います!
『こっちの方がいいんじゃないみたいな指摘』は大歓迎ですから,いつで伝えにきてください!
ありがとうございました!
次回
前回