🛒

Exception has occurred.DriftRemoteException (SqliteException(1): whil

2025/01/01に公開

driftにカラムを追加するとエラーが発生?

driftsqfliteを使用した開発をしているときに、データベースの設計を変更するとエラーが発生することがある。

drift + go_router_builderを使用したモックのECアプリを作っているときに発生したエラーです。

Githubのソースコード

Exception has occurred.
DriftRemoteException (SqliteException(1): while preparing statement, no such table: favorite_items, SQL logic error (code 1)
Causing statement: SELECT * FROM "favorite_items" WHERE "product_id" = ?;)

このエラーは、データベースのスキーマが更新されていないために発生しています。新しいテーブルを追加した場合、スキーマバージョンを上げて、マイグレーションを実装する必要があります。

公式に解説があった

// 変更前

  int get schemaVersion => 1;

// 変更後

  int get schemaVersion => 2;

データベースファイルを以下のように更新します:

import 'dart:io';
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as p;

part 'database.g.dart';

class CartItems extends Table {
  IntColumn get id => integer().autoIncrement()();
  TextColumn get productId => text()();
  TextColumn get name => text()();
  RealColumn get price => real()();
  TextColumn get imageUrl => text()();
  IntColumn get quantity => integer()();
  DateTimeColumn get addedAt => dateTime()();
}

class FavoriteItems extends Table {
  IntColumn get id => integer().autoIncrement()();
  TextColumn get productId => text().unique()();
  TextColumn get name => text()();
  RealColumn get price => real()();
  TextColumn get imageUrl => text()();
  TextColumn get description => text()();
  DateTimeColumn get addedAt => dateTime()();
}

(tables: [CartItems, FavoriteItems])
class AppDatabase extends _$AppDatabase {
  AppDatabase() : super(_openConnection());

  
  int get schemaVersion => 2;

  
  MigrationStrategy get migration => MigrationStrategy(
        onCreate: (Migrator m) async {
          await m.createAll();
        },
        onUpgrade: (Migrator m, int from, int to) async {
          if (from < 2) {
            await m.createTable(favoriteItems);
          }
        },
        beforeOpen: (details) async {
          await customStatement('PRAGMA foreign_keys = ON');
        },
      );

  // Cart operations
  Future<List<CartItem>> getAllCartItems() => select(cartItems).get();

  Future<int> addToCart(CartItemsCompanion item) => into(cartItems).insert(item);

  Future<int> removeFromCart(int id) => 
      (delete(cartItems)..where((t) => t.id.equals(id))).go();

  Future<int> updateQuantity(int id, int quantity) =>
      (update(cartItems)..where((t) => t.id.equals(id)))
      .write(CartItemsCompanion(quantity: Value(quantity)));

  Stream<List<CartItem>> watchCartItems() => select(cartItems).watch();

  // Favorite operations
  Future<List<FavoriteItem>> getAllFavorites() => select(favoriteItems).get();

  Future<bool> isFavorite(String productId) async {
    final count = await (select(favoriteItems)
          ..where((t) => t.productId.equals(productId)))
        .get();
    return count.isNotEmpty;
  }

  Future<int> addToFavorites(FavoriteItemsCompanion item) => 
      into(favoriteItems).insert(item);

  Future<int> removeFromFavorites(String productId) =>
      (delete(favoriteItems)..where((t) => t.productId.equals(productId))).go();

  Stream<List<FavoriteItem>> watchFavorites() => select(favoriteItems).watch();
}

LazyDatabase _openConnection() {
  return LazyDatabase(() async {
    final dbFolder = await getApplicationDocumentsDirectory();
    final file = File(p.join(dbFolder.path, 'cart.sqlite'));
    return NativeDatabase.createInBackground(file);
  });
}

データベースの設計を変更してお気に入り機能を実装してみました。設計を途中で変更するとエラーが発生するのは悩ましいですね💦

最初からテーブル設計をしておく必要がありますね。業務だとAPIから取得したテキストや画像をローカルデータベースを使用して端末に保存してネットワークに接続していなくても確認できるように使われることがあります。

ローカルデータベースを使うことはあまり業務ではないのですが、よく見かける自社開発企業のブログだとローカルにデータをキャッシュする目的で、isarやsqfliteを使うことがあるようです。isarはもうメンテナンスされていないので、driftかObjectBoxが技術選定するときの候補になりそうですね。

作ったアプリはこんな感じです。


Discussion