[Flutter]「Floor」AndroidのRoomチックなライブラリを導入してみる

突然ですが、このブログ初めてのFlutterカテゴリーです。

皆さん、Flutterやってますか?Flutter。

自分はつい最近始めてみました。

始めてみたって言っても、まだ20時間くらいしかコード書いてないし、そもそもUI周りを全くと言って触っていないので、果たしてFlutterを始めたの?って言えるかどうだかわからない状態ですが。

Dart始めました!

の方がしっくり来るかもしれないw

ホットリロードは糞早ぇぇぇぇぇ!!!ってものすごい実感しているけど。

ってことで、なんでFlutterを始めたかと言うと個人アプリのiOS版を出したくなってしまったからw

iOS版出したいって言ってもMAC持ってないからipaファイル作れないし、そもそもストア申請出来ないんだけどね!(死

そのうち手に入れたら申請するよ。。。アプリ自体全くできてないけど、、、。

ってことで、本題(前書きが長いw)。

個人アプリではDatabaseにRoomを使っている。

で、Flutterにも似た内容のライブラリ無いかな?って思ったらほぼRoomと同じ使い勝手のライブラリのFloorを見つけたから導入してみたって話。

1テーブル作って、とりあえず出来たぞー!!ってところでこの記事書き始めてしまったから色々吟味はしていないから間違えているところはあるかもしれないw

ってことで、導入手順。今回も個人アプリに使う(予定)のコードを晒していきますw

基本的に↑のRead meにそっていけば大丈夫。

Roomについてはこちら。

スポンサーリンク

導入手順

依存

dependencies:
  # DB
  floor: ^0.9.0

dev_dependencies:
  # DB
  floor_generator: ^0.9.0
  build_runner: ^1.7.1

floorのパッケージ自体がSqliteとかPathとかに依存しているので 自分でSqliteに依存させる必要性はないみたい。

Flutterのお作法はまだわかってないけど、多分、Sqliteは書かなくて良い。

ビルド通ったしw

Entityクラスを作成する

// 現在のセッション
@Entity(tableName: 'CurrentSession')
class CurrentSession {
  CurrentSession(this.userId, this.name, this.token, this.secret);

  @primaryKey
  final String userId;

  final String name;

  final String token;

  final String secret;

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is CurrentSession &&
          runtimeType == other.runtimeType &&
          userId == other.userId &&
          name == other.name &&
          token == other.token &&
          secret == other.secret;

  @override
  int get hashCode =>
      userId.hashCode ^ name.hashCode ^ token.hashCode ^ secret.hashCode;

  @override
  String toString() {
    return 'CurrentSession{userId: $userId, name: $name, token: $token, '
        'secret: $secret}';
  }
}

Entityの作り方は基本的にRoomと一緒。

PrimaryKeyを複数にしたい場合はannotationを@Entity(primaryKeys :[‘userId’, ‘name’])って書けば良い。

Daoを作成する

// 現在のセッション
@dao
abstract class CurrentSessionDao {
  @Query('SELECT * FROM CurrentSession limit 1')
  Future<CurrentSession> getCurrentSession();

  @transaction
  Future<void> replace(CurrentSession session) async {
    await deleteSession();
    await saveSession(session);
  }

  @insert
  Future<void> saveSession(CurrentSession session);

  @Query('DELETE FROM CurrentSession')
  Future<void> deleteSession();
}

insertでconflictした際の対応(onConflict = OnConflictStrategy.REPLACE)がなさそうなので自前で実装するっぽい。

transactionでDeleteしてSave。

このテーブルはSessionを1つにしたいから全部削除しちゃってるけど、特定のユーザーだけ消したいとかだったらDelete文でWhere句を書けば大丈夫のはず。

Databaseクラスを作成する

// required package imports
import 'dart:async';
import 'package:floor/floor.dart';
import 'package:kataomoi/db/entity/current_session.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart' as sqflite;

import 'dao/current_session_dao.dart';

part 'kataomoi_database.g.dart'; // the generated code will be there

@Database(version: 1, entities: [CurrentSession])
abstract class AppDatabase extends FloorDatabase {
  static AppDatabase _appDatabase;

  CurrentSessionDao get currentSessionDao;

  static Future<AppDatabase> getDataBase() async {
    return _appDatabase ??=
        await $FloorAppDatabase.databaseBuilder('kataomoi.db').build();
  }
}

importの上5行は必須で書かないといけないっぽい。Read meにも書いてある。

使いたいDaoを記載して、DBを取得するメソッドを書いておく。

自動生成ファイルを作成する

↑のpart ‘kataomoi_database.g.dart’; 部分が自動生成ファイル。以下のコマンドをプロジェクトルートで叩く。

flutter packages pub run build_runner build

kataomoi部分はおそらくyamlファイルの一番上のnameの属性に書かれているところだと思う。

最初、Read me通りに database.g.dart って記載したら、そんなファイルねぇよ!って怒られてハマったw

使ってみる

実装

        final database = await AppDatabase.getDataBase();
        final session = await database.currentSessionDao.getCurrentSession();
        print('hoge 1 = $session');
        await database.currentSessionDao
            .replace(
            CurrentSession('1001', '1001_name', '1001_token', '1001_secret'));
        final session2 = await database.currentSessionDao.getCurrentSession();
        print('hoge 2 = $session2');
        await database.currentSessionDao
            .replace(
            CurrentSession('1000', '1000_name', '1000_token', '1000_secret'));
        final session3 = await database.currentSessionDao.getCurrentSession();
        print('hoge 3 = $session3');

acyncブロック内で書きます。

上記のログ

I/flutter ( 5272): hoge 1 = null
I/flutter ( 5272): hoge 2 = CurrentSession{userId: 1001, name: 1001_name, token: 1001_token, secret: 1001_secret}
I/flutter ( 5272): hoge 3 = CurrentSession{userId: 1000, name: 1000_name, token: 1000_token, secret: 1000_secret}

hoge 1は初回なので、なにもないからnull。

hoge 2は入れたのがsaveしたのが帰ってくる。

hoge 3は最後にsaveしたのが帰ってくる。

個人的には想定通りの動きをしてくれている。

floorで出来ないこと(多分)

Insert時のオプション

少し書いたけど、プライマリーキーが被ったときとかどうするかの挙動が書けないっぽい。

replase?roolback?その他諸々。

個人アプリではそんなに気にするところではないから、気にしないけどw

RoomのConverter相当のものがない

EntityにListとかDataTimeとか持たせてConverterで自動変換させてinsertする手法が取れない。

Entity作る前に自前でListとかをString型とかに変換させないといけない

ビルド時に自動生成ファイルを生成してくれない

当たり前っちゃ当たり前だけど、、、。

ビルド時に自走生成してくれたらホットリロード多分出来ないしね。

Androidと違って自分でコマンド打つしか無いと思う。

floorを使ってみての所感

Roomを使ったことある人なら入れるべきかなぁって思う。

使い方がほぼ一緒だし。

生SQLiteを触るよりかは楽な気もする。

ただ以下の記載があって、APIが変わる可能性あるから要注意。

「 This package is still in an early phase and the API will likely change. 」

個人アプリレベルで使う分にはもってこいのライブラリだけどw

タイトルとURLをコピーしました