💨

【Flutter】SQLiteを用いたトランザクションとバッチの処理の違い

2025/02/12に公開

使用するライブラリ

https://pub.dev/packages/sqflite

基本的なトランザクションとバッチの違いに関して

トランザクションとバッチの違いを表にまとめました。

基本的な違い

項目 トランザクション バッチ処理
目的 データの整合性を保つ 処理の効率化
実行方法 個別の操作を順番に実行 複数の操作をまとめて実行
主な用途 送金処理、在庫管理など データの一括登録、一括更新

使用シーン

状況 トランザクション バッチ処理 説明
データの整合性が重要 トランザクションの方が制御しやすい
大量データの処理 バッチ処理の方が効率的
条件分岐が必要 × トランザクションで制御する
一括更新 バッチ処理が適している

sqflite でのトランザクション

トランザクションは sqflite の基本的な機能の一つで、データベースの一貫性を保証するために使用されます。トランザクションはBEGIN TRANSACTIONで開始し、COMMITで終了します。

transaction_example.dart
// トランザクションの基本的な使用例
Future<void> transferMoney(int fromAccount, int toAccount, double amount) async {
  final db = await database;

  await db.transaction((txn) async {
    // 送金元の残高確認
    final senderBalance = await txn.query(
      'accounts',
      columns: ['balance'],
      where: 'id = ?',
      whereArgs: [fromAccount]
    );

    if (senderBalance.first['balance'] < amount) {
      // 例外をスローすることで、トランザクションが自動的にロールバックされます
      throw Exception('残高不足です');
    }

    // 送金元から引き出し
    await txn.update(
      'accounts',
      {'balance': senderBalance.first['balance'] - amount},
      where: 'id = ?',
      whereArgs: [fromAccount]
    );

    // 送金先に入金
    await txn.update(
      'accounts',
      {'balance': senderBalance.first['balance'] + amount},
      where: 'id = ?',
      whereArgs: [toAccount]
    );
  });
}

トランザクションの重要な特徴は、すべての処理が成功するか、すべての処理が行われないかのどちらかということです。途中でエラーが発生した場合、それまでに行われた変更はすべて取り消されます。

sqflite でのバッチ処理

バッチ処理は、複数の SQL 文を一度に実行するための機能です。また、今回はデータの整合性をあわせるために、トランザクションとバッチの両方を実装しています。バッチ処理を一つのトランザクション内で複数行う場合は、複数の batch の変数の宣言を行う必要があります。

batch_example.dart
Future<void> createNewUserWithSettings(User user, List<UserSetting> settings) async {
  final db = await database;

  // バッチ処理はトランザクション内で実行可能
  await db.transaction((txn) async {
    final batch = txn.batch();

    // ユーザーの作成
    batch.insert('users', user.toMap());

    // ユーザー設定の一括登録
    for (var setting in settings) {
      batch.insert('user_settings', setting.toMap());
    }

    // commitが実行されてから、上記のbatchの処理が実行される。
    // (トランザクション内なのでここではまだ確定しない)
    await batch.commit();

    // トランザクションが終了するときに全ての変更が確定します
  });
}

使い分けのガイドライン

  1. トランザクションを使用すべき場合:

    • データの整合性が重要な処理
    • 条件に基づいて処理を分岐させる必要がある場合
    • クエリ結果を確認してから更新を行う場合
  2. バッチ処理を使用すべき場合:

    • 大量のデータを一括で処理する場合
    • 処理の順序が決まっていて条件分岐が不要な場合
    • パフォーマンスの最適化が必要な場合
  3. 組み合わせが有効な場合:

    • 大量データの処理と整合性チェックの両方が必要な場合
    • 複数のバッチ処理を小さい単位で実行したい場合
    • 処理の一部で条件分岐が必要な場合

この使い分けを適切に行うことで、効率的かつ安全なデータベース操作を実現できます。

参照

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

https://pub.dev/documentation/sqflite/latest/sqflite/Batch-class.html

Discussion