Flutter: リストア(インポート)したはずのsqfliteの「.db」ファイルからデータを読み込めない

※当サイトは、アフィリエイト広告を利用しています

結論:リストア前に「deleteDatabase」で旧DBを削除し、リストア後に「openDatabase」で新DBを再オープンする

2022/6/12 Flutter エラー・バグ日記

 

ローカルDBに「sqflite」を用いているアプリで、データベースファイル(以下、「.db」ファイル)のバックアップと、リストア/インポート(以下、リストア)の機能を実装しているが、実は、リストアが正常動作していないことが発覚。

 

本記事はライトな日記思考で書いているので、詳細説明はしておらず、基本、テキストのみで画像とかは載せておりません。。m(_ _)m

解説記事ではないため、解決していない内容や、その時々の間違った解釈を述べてしまっている可能性が大いにありますので、何卒、ご了承ください。

 

非常に大まかだが、リストアの処理は、以下の方法で行っていた。

 

  1. Googleドライブ上にバックアップされている「.db」ファイルを、「googleapis」パッケージの「files.get」メソッドを使い、アプリ内に読み込む
  2. リストア先のパス(既にDBが存在する場合は、そのDBのパスと同じ)を指定した「File」クラスのインスタンスを作成
  3. 上記1.で読み込んだデータを、上記2.のインスタンスに「writeAsBytes」メソッドで書き込む(既存のDBが存在する場合は、同じファイル名「〜.db」で上書きされる)

 

この状態でリストアを実行後、「.db」ファイルから読み込んだデータを表示する画面に遷移すると、データが表示されるときと、表示されないときがあることが判明(アプリを再起動すれば、表示される)。

 

「Device File Explorer」で確認すると、データが表示されるとき・されないときのいずれも、リストアした「.db」ファイルはきちんと存在しており、「DB Brower for SQLite」を使って中身を見ても、期待どおりのデータが格納されている。

 

DBファイル自体は、きちんとリストアできている。なぜ?と詰まる。

 

色々なパターンで検証した結果、アプリのインストール後に、データベースを開く「openDatabase」メソッドを1回以上実行した後にリストアすると、「.db」ファイルからデータを読み込めないと分かった。

 

逆に、アプリのインストール後、「openDatabase」メソッドを1回も実行しない状態でリストアしたときは、問題なく読み込めていた

 

よく考えると、「sqflite」のリストア方法をきちんと調査していなかった。。そこで、改めて調べてみると、こちらの情報を見つけた。

 

 

上記記事では、更に以下の「sqflite」の公式説明ページが参照されていた。

 

 

この情報によると、「.db」ファイルを差し替えるときは、

 

  • 旧ファイルを、「deleteDatabase」メソッドで削除しておく
  • 新ファイルを、「openDatabase」メソッドで再オープンする

 

という処理が必要らしい。

 

恐らく、この処理をやらずに、アプリインストール後に、いったん「openDatabase」をしてしまうと、その後で新しい「.db」ファイルをリストアしても、データベースのインスタンスが古い状態のまま更新されないので、データを読み込めなくなるのだろう、、、と理解。

 

てっきり「.db」ファイルを上書きする(差し替える)だけで、新しいデータを読み込めると思い込んでいたが、そうではなかった。。。

 

よく考えると、別のローカルデータベースである「hive」のバックアップ・リストアの処理を実装したときも、同様の理由でリストアがうまく行かなかったことを思い出した。

 

(↓こちらご参考)

 

結果、リストアの処理を、以下のように修正したところ、どのような条件下でも、リストア後の「.db」ファイルからデータを読み込きるようになった。

 

// クラス・メソッド(関数)・プロパティ(変数)について、筆者が作成したものには、末尾に大文字のオー「O」をつけて表記
// ※パッケージで予め決められているものと区別しやすくするため

// CRUD処理の記述に必要なDatabase?型のインスタンス作成
static Database? databaseO;

// ①
// リストア前にDBファイルをクローズする処理
// DBファイルをアプリ内専用フォルダに保存していた場合の例
Directory documentsDirectoryO = await getApplicationDocumentsDirectory();
String fullDbPathO = join(documentsDirectoryO.path, "databaseFile.db");
await deleteDatabase(fullDbPathO);

// ②
// 外部から読み込んだデータを、リストア前と同じパスで保存
// ここではdataStoreOは、外部から読み込んだList<int>型のデータ ※詳細は省略
final importFileO = File(fullDbPathO);
await importFileO.writeAsBytes(dataStoreO);

// ③
// 上記パスのDBファイルをオープンする処理
databaseO = await openDatabase(
      fullDbPathO,
      version: databaseVersionO,   // データベースのバージョン ※詳細は省略
      onCreate: onCreateO,  // テーブルを作成するメソッド ※詳細は省略
    );

 

リリースしたアプリ(全てFlutterで開発)

 

個人アプリ開発で役立ったもの

おすすめの学習教材

超初心者向けでオススメな元Udemyの講座/

 

 \キャンペーン時を狙えば安価で網羅的な内容が学べる(日本語訳あり)/

 

\Gitの基礎について無料で学べる/

 

おすすめの学習書籍

実用的image_pickerに関してかなり助けられた/

 

Dartの基礎文法を素早くインプットできる/


Dart入門 - Dartの要点をつかむためのクイックツアー

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