📑

【Flutter】SQLiteでデータベースを組み込む方法

2025/01/27に公開

使用するライブラリ

https://pub.dev/packages/sqflite

データベースを組み込む方法

1. アセットの準備

まず、プロジェクトにデータベースファイルを追加します。

assets/example.db

pubspec.yamlにアセットを登録します:

pubspec.yaml
flutter:
  assets:
    - assets/example.db

2. データベースの実装パターン

パフォーマンス重視の実装

初回起動時のみデータベースをコピーする方法です。

performance_database.dart
// データベースを初期化する関数
Future<Database> initPerformanceDatabase() async {
  // データベースのパスを取得
  var databasesPath = await getDatabasesPath();
  var path = join(databasesPath, "demo_asset_example.db");

  // データベースの存在確認
  var exists = await databaseExists(path);

  if (!exists) {
    print("新規データベースをコピーします");

    try {
      // 親ディレクトリの作成
      await Directory(dirname(path)).create(recursive: true);

      // アセットからコピー
      ByteData data = await rootBundle.load(url.join("assets", "example.db"));
      List<int> bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);

      // データベースファイルの書き込み
      await File(path).writeAsBytes(bytes, flush: true);
    } catch (e) {
      print("エラーが発生しました: $e");
    }
  }

  // データベースを開く
  return await openDatabase(path, readOnly: true);
}

毎回新規コピーする実装

アプリ起動時に必ず新しいデータベースをコピーする方法です。既存のデータベースを削除する手順が必要です。

fresh_copy_database.dart
// 毎回新しいデータベースをコピーする関数
Future<Database> initFreshDatabase() async {
  var databasesPath = await getDatabasesPath();
  var path = join(databasesPath, "demo_fresh_copy.db");

  // 既存のデータベースを削除
  await deleteDatabase(path);

  try {
    // ディレクトリ作成
    await Directory(dirname(path)).create(recursive: true);

    // アセットからコピー
    ByteData data = await rootBundle.load(url.join("assets", "example.db"));
    List<int> bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
    await File(path).writeAsBytes(bytes, flush: true);
  } catch (e) {
    print("エラーが発生しました: $e");
  }

  return await openDatabase(path, readOnly: true);
}

バージョン管理機能付きの実装

データベースのバージョンを管理する方法です。
既存 DB と AssetDB  の Version を比較して、DB を追加・変更したい場合に使用する。

versioned_database.dart
// バージョン管理付きデータベース初期化関数
Future<Database> initVersionedDatabase({
  required String databasesPath,
  required String versionNumFilename,
  required String dbFilename
}) async {
  var dbPath = join(databasesPath, dbFilename);
  var versionNumFile = File(join(databasesPath, versionNumFilename));

  // 現在のバージョン確認
  var existingVersionNum = 0;
  if (versionNumFile.existsSync()) {
    existingVersionNum = int.parse(await versionNumFile.readAsString());
  }

  // アセットのバージョン取得
  var assetVersionNum = int.parse(
    (await rootBundle.loadString(url.join('assets', versionNumFilename))).trim()
  );

  // バージョン比較とコピー
  if (existingVersionNum < assetVersionNum) {
    try {
      await Directory(databasesPath).create(recursive: true);

      var data = await rootBundle.load(url.join('assets', dbFilename));
      var bytes = Uint8List.sublistView(data);

      await File(dbPath).writeAsBytes(bytes, flush: true);
      await versionNumFile.writeAsString('$assetVersionNum', flush: true);
    } catch (e) {
      print("エラーが発生しました: $e");
    }
  }

  return await openDatabase(dbPath);
}

使い分けのポイント

  1. パフォーマンス重視の実装

    • メモリ使用量を最小限に抑えたい場合
    • データベースの更新頻度が低い場合
  2. 毎回新規コピーの実装

    • 常に最新のデータを使用したい場合
    • テスト環境で使用する場合
  3. バージョン管理機能付きの実装

    • アプリのアップデートでデータベースも更新する場合
    • データベースの変更履歴を管理したい場合

参照

https://github.com/tekartik/sqflite/blob/master/sqflite/doc/opening_asset_db.md

Discussion