web-dev-qa-db-ja.com

混乱:SQLiteOpenHelper onUpgrade()はどのように動作しますか?そして、古いデータベースのバックアップのインポートと一緒に?

sQLiteOpenHelperに2つの列と対応する作成スクリプトを持つデータベーステーブルtest_tableがあると仮定します。

DB_VERSION = 1:
public void onCreate(SQLiteDatabase db)
{
db.execSql("CREATE table test_table (COL_A, COL_B);
}

これは、Playストアで公開されている最初のアプリバージョン1です。

しばらくすると、アプリと使用されているデータベースが更新されます。 SQLiteOpenHelperクラスは次のように調整する必要があると思います。

DB_VERSION = 2:
public void onCreate(SQLiteDatabase db)
{
db.execSql("CREATE table test_table (COL_A, COL_B, COL_C)");
}

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
db.execSql("ALTER TABLE test_table ADD Column COL_C");
}

しばらくすると、別のアプリが更新されます。

DB_VERSION = 3:
public void onCreate(SQLiteDatabase db)
{
db.execSql("CREATE table test_table (COL_A, COL_B, COL_C, COL_D)");
}

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
db.execSql("ALTER TABLE test_table ADD Column COL_D");
}

->ここでアドバイスが必要です。ユーザーがアプリバージョン1をインストールすると、列AとBがあります。その後バージョン2に更新すると、onUpgradeが起動して列Cが追加されます。最初からインストールする新しいユーザーは、createステートメントを介して3つの列を取得します。その後、ユーザーがバージョン3に更新すると、onUpgradeが再度起動され、列Dが追加されます。しかし、ユーザーがアプリバージョン1をインストールしてから、バージョン2の更新をスキップし、バージョン3を更新した場合はどうなりますか?それから彼は逃したでしょう

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)

    {
    db.execSql("ALTER TABLE test_table ADD Column COL_C");
    }

一部のみ

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)

    {
    db.execSql("ALTER TABLE test_table ADD Column COL_D");
    }

呼び出され、テーブルtest_table(COL_A、COL_B、COL_D)につながりますか?

ユーザーがデータを失わないように、ライブアプリのデータベースアップグレードを処理する正しい方法は何ですか? onUpgrade()メソッドで可能なすべての(古い)バージョンをチェックし、そのバージョンに基づいて異なるalter tableステートメントを実行する必要がありますか?

私のアプリでは、ユーザーがデータをエクスポートおよびインポートする可能性があるため、これはエクスポートにすぎません。データベース全体をコピーしてインポートします。アプリデータベースをバックアップコピーデータベースに置き換えます。

ユーザーがアプリバージョン1を使用していて、データベースをエクスポートし、アプリをアップグレードして(新しいデータベース構造)、古いバージョン1のバックアップをインポートするとどうなりますか? -> SQLiteOpenHelperはどのように動作しますか? ->インポート/エクスポート機能と一緒にdbアップグレードを処理する正しい方法は何ですか?

27
Toni Kanoni

ユーザーがデータを失わないように、ライブアプリのデータベースアップグレードを処理する正しい方法は何ですか? onUpgrade()メソッドで可能なすべての(古い)バージョンをチェックし、そのバージョンに基づいて異なるalter tableステートメントを実行する必要がありますか?

概して、そうです。

これに対する一般的なアプローチは、ペアワイズアップグレードを行うことです。

_public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  if (oldVersion<2) {
    // do upgrade from 1 to 2
  }

  if (oldVersion<3) {
    // do upgrade from 2 to 3, which will also cover 1->3,
    // since you just upgraded 1->2
  }

  // and so on
}
_

これは、たとえばRails移行とほぼ同じです。

ユーザーがアプリバージョン1を使用していて、データベースをエクスポートし、アプリをアップグレードして(新しいデータベース構造)、古いバージョン1のバックアップをインポートするとどうなりますか? -> SQLiteOpenHelperはどのように動作しますか?

「データベース全体をコピーする」とは、文字通りSQLiteデータベースファイルの完全なファイルコピーを意味する場合、SQLiteOpenHelperが復元されたバックアップを開くと、データベースのスキーマバージョンが古いものになります。通常どおりonUpgrade()を実行します。

インポート/エクスポート機能と一緒にdbアップグレードを処理する正しい方法は何ですか?

答えは、ファイル全体をコピーしてバックアップを作成するか、SQLiteDatabaseオブジェクトでgetVersion()を呼び出すことで取得できるスキーマバージョンをバックアップおよび復元するように手配することだと思います。そうは言っても、私はこのシナリオをあまり扱っていませんし、私が考えていない問題がもっとあるかもしれません。

33
CommonsWare

以下の擬似コードは、精神的なアップグレードを示しています

@Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        switch(oldVersion) {

        case 2:
                //upgrade logic from version 2 to 3
        case 3:
                //upgrade logic from version 3 to 4
        case 4:
                //upgrade logic from version 4 to 5
                break;
        default:
                throw new IllegalStateException(
                "onUpgrade() with unknown oldVersion" + oldVersion));
        }
    }

インクリメンタルアップグレードとは、つまり、ケース2と3のbreakステートメントが欠落していることに注意してください。

たとえば、古いバージョンが2で、新しいバージョンが4の場合、ロジックはデータベースを2から3にアップグレードし、次に4にアップグレードします。

古いバージョンが3で、新しいバージョンが4の場合、3から4のアップグレードロジックを実行するだけです。

重大な変更を行う新しいデータベースバージョンのアップグレードごとに、新しいケースを追加し続けます

19
Aun

必要なすべての列を含む新しいテーブルを作成することもできると思います

CREATE TABLE new_test_table (COL_A, COL_B, COL_C,COL_D);

古いテーブルから新しいテーブルにデータをコピーします

INSERT INTO new_test_table SELECT * FROM test_table;

古いテーブルを削除しますDROP TABLE test_table;

新しいテーブルの名前を変更します

ALTER TABLE new_test_table RENAME TO test_table;

要するに

public void onUpgrade(SQLiteDatabase db,int OldVersion,int NewVersion){
  db.execSQL("CREATE TABLE new_test_table (COL_A, COL_B, COL_C,COL_D);"+
             "INSERT INTO new_test_table SELECT * FROM test_table;"+
             "DROP TABLE test_table;"+
             "ALTER TABLE new_test_table RENAME TO test_table;");"
    }

そうすれば、データの損失や増分の変更について心配する必要はありません。

3
Fuseteam

この質問に答えるには遅すぎると思いますが、他の人を助けるかもしれません。

     DB_VERSION = 1:
        public void onCreate(SQLiteDatabase db)
        {
        db.execSql("CREATE table test_table (COL_A, COL_B, COL_C, COL_D, COL_E);
        }


      public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
        {
             switch (oldVersion) {
        case 1:                     db.execSql("ALTER TABLE test_table ADD Column COL_C");
                                    db.execSql("ALTER TABLE test_table ADD Column COL_D");
                                    db.execSql("ALTER TABLE test_table ADD Column COL_E");
                            break;
        case 2:                     db.execSql("ALTER TABLE test_table ADD Column COL_D");
                                    db.execSql("ALTER TABLE test_table ADD Column COL_E");
                            break;
        case 3:                     db.execSql("ALTER TABLE test_table ADD Column COL_E");

                            break;

and so on...
}

ユーザーが初めてアプリをインストールした場合、onCreateからすべての列を取得します

データベース2の古いバージョンの場合、ケース2は列CとDを追加します。

古いバージョンのデータベース3の場合、ケース3は列Eのみを追加します

このようにして、データベースのバージョンについて混乱することはありません。

0
umi