FlutterのデータベースライブラリのMoorを選択する方がいると思いますが、今記事では、どのように"Foreign Key"を設定するのか、1対多、多対多はMoorでどう実現するのかを見ていきます。
対象読者
- moorの設定方法がある程度理解できている人。
- moorのテーブル作成をしたことがある人。
- daoでクエリを記述したことのある人。(知らなくても大丈夫かも)
- 外部キーの方法で困っている人。
※今回は、基本的なmoorのことは省いています。(コード自動生成など)
1. データベース設定
初めにデータベースの設定を行なっていきます。
この設定は、moorの公式ホームページを見てもらったら簡単に設定できると思います。
Moor公式HP
@UseMoor(tables: [CategoryRecords, SubCategoryRecords], daos: [DaoQuery])
class MyDatabase extends _$MyDatabase {
MyDatabase() : super(_openConnection());
@override
int get schemaVersion => 1;
@override
MigrationStrategy get migration => MigrationStrategy(
onCreate: (detail) {
//一番初めにアプリを明け、データベースにアクセスするタイミングで呼ばれる。
//この記述なくても発火してくれるため、書く必要なし。
return detail.createAll();
},
//毎回アプリを明けデータベースにアクセスするタイミングで呼ばれる。
beforeOpen: (details) async {
await customStatement('PRAGMA foreign_key = ON');
}
});
}
LazyDatabase _openConnection() {
return LazyDatabase(() async {
final dbFolder = await getApplicationDocumentsDirectory();
final file = File(p.join(dbFolder.path, 'db.sqlite'));
return VmDatabase(file);
});
}
moorに慣れている人にはお馴染みのコードですが、MigrationStrategyなるものが追加されています。
onCreate関数は、「1番初めのアプリ起動時かつ、データベースに初めてアクセスするタイミング」で呼ばれます。なので、一度アプリをキルし、2度目アプリを開き、初めてデータベースにアクセスした際は呼ばれません。
beforeOpen関数は、「アプリを起動しデータベースに初めてアクセスするタイミング」で必ず呼ばれるものになります。
この関数の場合は、一度アプリをキルし、2度目アプリを開き、初めてデータベースにアクセスした際は呼ばれます。
beforeOpen関数内で、
customStatement('PRAGMA foreign_key = ON');
との記述があります。
sqliteはデフォルトで外部キーが設定されていない(OFF)ようなので、このデータベースに初めてアクセスするタイミングで、外部キー制約をONにしなければいけないようです。
これで、下準備は完了です。
次に実際にテーブルを記述していきます。
2. テーブル作成。
今回は、
主テーブル: CategoryRecords
小テーブル: SubCategoryRecords
の二つのテーブルを作成し、1対多の関係を作っていきます。
class CategoryRecords extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get name => text().withLength(max: 100)();
RealColumn get experience => real()();
}
class SubCategoryRecords extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get name => text().withLength(max: 100)();
IntColumn get level => integer()();
BoolColumn get nowLevel => boolean()();
IntColumn get categoryId =>
integer().customConstraint('NOT NULL REFERENCES categoryRecords(id)')();
}
SubCategoryRecordテーブルに外部キーを設定しています。
moor自体には、dartのコードで外部キー制約することができないみたいなので、
integer.customConstraint('NOT NULL REFERENCES categoryRecords(id)')();
のように、customConstraintを使用して、独自で外部キー制約をカスタムしないといけないようです。
これで、SubCategoryRecordsは、CategoryRecordsのidを外部キーとしてもつことができました。
その後、コード生成してもらえたら大丈夫です。
3,データ取得クエリ(例)
それでは、コード生成し終え、テーブル内にすでにデータが入っていることを想定して2つのテーブルをjoinしデータを取得する方法を見ていきましょう。
クエリは、daoを使用し別Classで記述しています。
import 'package:demo_flutter_project/models/db/practice/database.dart';
import 'package:demo_flutter_project/models/db/practice/tables/category_records.dart';
import 'package:demo_flutter_project/models/db/practice/tables/sub_category_records.dart';
import 'package:moor/moor.dart';
part 'dao_query.g.dart';
//2つのテーブルから取得できたデータを格納するClassを作成。
class MainAndSubCategory {
final CategoryRecord categoryRecord;
final SubCategoryRecord subCategoryRecord;
MainAndSubCategory(this.categoryRecord, this.subCategoryRecord);
}
class DaoQuery extends DatabaseAccessor<MyDatabase> with _$DaoQueryMixin {
DaoQuery(MyDatabase db) : super(db);
//2つのテーブルをjoinしてデータを取得する方法。
Future<List<MainAndSubCategory>> getAllMainAndSubCategory() async {
//subCategoryRecordsテーブルを軸にjoinメソッドでcategoryRecordsをjoinする。
final query = select(subCategoryRecords).join([
//categoryRecordsのidとsubCategoryRecordsのcategoryIdが一致しているものに対して。
//leftOuterとrightOuterも用意されている。
innerJoin(categoryRecords,
categoryRecords.id.equalsExp(subCategoryRecords.categoryId))
]);
//queryを使用し、getでデータ取得。(get()もしくは、watch()でデータを取得可能。)
final typedResult = await query.get();
final List<MainAndSubCategory> categoryList = [];
//mapではできなかった。(理由は詳しい方教えてください。。。)
for (var result in typedResult) {
categoryList.add(MainAndSubCategory(
//以下のように、それぞれのテーブルから取得してきたデータを取得できる。
result.readTable(categoryRecords),
result.readTable(subCategoryRecords)));
}
return categoryList;
}
}
クエリの記述方法はこんな形になります。
queryを使用してgetする。。。
あたりからは、人によって記述方法が変わってくると思います。
今回は、get()で取得したのですが、watchでもデータ取得できますが、公式にガッツリ方法が記述されているので今回はget()で記述しました。
終わり
今回はmoorを使用して外部キー制約を見ていきました。
自分もmoorはあまり触ったことがなく、間違っている部分があると思いますのでご指摘お願いいたします。
また、seed値のようなものをmoorで作成できる方法があればご教授願いたいです。
ありがとうございました。
参考文献