Android:DBバージョンのアップグレードと新しいテーブルの追加


117

アプリ用のsqliteテーブルを既に作成しましたが、データベースに新しいテーブルを追加したいと思います。

以下のようにDBバージョンを変更しました

private static final int DATABASE_VERSION = 2;

テーブルを作成するための文字列を追加しました

private static final String DATABASE_CREATE_color = 
   "CREATE TABLE IF NOT EXISTS files(color text, incident_id text)";

onCreateそしてonUpgrade以下の通り:

@Override
    public void onCreate(SQLiteDatabase database) {
        database.execSQL(DATABASE_CREATE_incident);
        database.execSQL(DATABASE_CREATE_audio);
        database.execSQL(DATABASE_CREATE_video);
        database.execSQL(DATABASE_CREATE_image);

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        //drop table and add new tables when version 2 released.
        db.execSQL(DATABASE_CREATE_color);

    }

しかし、何らかの理由で新しいテーブルが作成されていません。何が悪いのですか?


これは別の興味深い解決策ですが、これまでのところ、私が見た中で最も堅牢なバージョンがここにあります
Suragch、2018

回答:


280

1. onCreate()およびonUpgrade()について

onCreate(..)アプリが新たにインストールされるたびに呼び出されます onUpgradeアプリがアップグレードされて起動され、データベースのバージョンが同じでない場合は常に呼び出されます

2. dbバージョンの増分

次のようなコンストラクタが必要です。

MyOpenHelper(Context context) {
   super(context, "dbname", null, 2); // 2 is the database version
}

重要:アプリのバージョンを上げるだけでは十分ではありませんonUpgrade

3.新しいユーザーを忘れないでください。

追加することを忘れないでください

database.execSQL(DATABASE_CREATE_color);

onCreate()メソッドに追加するか、新しくインストールしたアプリにはテーブルがありません。

4.時間の経過に伴う複数のデータベースの変更に対処する方法

アプリのアップグレードが連続して行われ、そのうちのいくつかがデータベースのアップグレードを持っている場合、必ず確認する必要がありoldVersionます。

onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
   switch(oldVersion) {
   case 1:
       db.execSQL(DATABASE_CREATE_color);
       // we want both updates, so no break statement here...
   case 2:
       db.execSQL(DATABASE_CREATE_someothertable); 
   }
}

このようにして、ユーザーがバージョン1からバージョン3にアップグレードすると、両方の更新が取得されます。ユーザーがバージョン2から3にアップグレードすると、リビジョン3のアップデートを取得するだけです。結局のところ、アップデートをリリースするたびにユーザーベースを100%アップグレードすることはできません。時々彼らは更新または12をスキップします:)

5.開発中にリビジョン番号を管理する

そして最後に...

adb uninstall <yourpackagename>

アプリを完全にアンインストールします。再インストールすると、ヒットonCreateすることが保証されます。これにより、開発中にデータベースバージョンを成層圏に増分し続ける必要がなくなります...


5
#4に関して:oldVersion渡された引数を使用する方が良いでしょうか?アップグレード文が繰り返し可能な場合、最新のデータベースでそれらを繰り返してしまう可能性があります。ステートメントの1つがテーブルを切り捨てることである場合、それは非常に悪いことです。
グレイソン

3
@グレイソン:素晴らしいポイント!正直なところ、それについて本当に考えたことがないので、少し馬鹿げています。必要な引数を使用し、残りの引数を無視する癖があると思うことがあります。
jkschneider 2011年

1
あなたはデータベースを管理していますが、なぜ名前を変更するのですか?
jkschneider 2015年

3
newVersionとにかくコンストラクタで現在のデータベースバージョンを常に設定しているため(パート2を参照)、常に一致するため、少し役に立たない。ここでの重要なアイデアは、ユーザーがnewVersion他のすべての増分アップグレードを経由せずに、ユーザーがどこへでも直接アップグレードできるようにしたくないということです。
jkschneider 2015年

2
@kai CREATE_READINGSロジックはonCreate最初のバージョンのメソッドにあったため、onUpgradeに含めないでください。onUpgradeスイッチ内のケースを「私はFROMをアップグレードしています」と考えてくださいoldVersion。既に存在しているはずなので、バージョン1からアップグレードする場合は、読み取り値テーブルを作成しません。うまくいけば、これは理にかなっています...
jkschneider

9

コードは正しいようです。私の提案では、データベースはすでにアップグレードされていると考えています。バージョン番号を増やした後でプロジェクトを実行したが、execSQL呼び出しを追加する前に、テストデバイス/エミュレーターのデータベースがすでにバージョン2であると信じている場合があります。

これを確認する簡単な方法は、バージョン番号を3に変更することです。その後アップグレードする場合は、デバイスがすでにアップグレードされていると信じていたためであることがわかります。


その後、予想通り、コードは問題ありませんでした。それが段階的に実行されたときだけではありません。onCreate()jkschneiderが指摘したように、テーブルの作成を忘れずに追加してください。
Greyson

2

SQLiteOpenHelperのonUpgradeメソッドを使用できます。onUpgradeメソッドでは、パラメーターの1つとしてoldVersionを取得します。

onUpgrade利用A switchとのそれぞれcaseのデータベースの現在のバージョンを追跡するためにバージョン番号を使用します。

からoldVersionにループし、一度に1つnewVersionずつ増分versionしてから、データベースを段階的にアップグレードすることをお勧めします。これは、データベースバージョン1を使用している誰かが、長い時間をかけてアプリをデータベースバージョン7を使用するバージョンにアップグレードし、互換性のない特定の変更によりアプリがクラッシュし始めた場合に非常に役立ちます。

次に、データベースの更新は段階的に行われ、可能なすべてのケースをカバーします。つまり、新しいバージョンごとに行われたデータベースの変更を組み込んで、アプリケーションのクラッシュを防ぎます。

例えば:

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    switch (oldVersion) {
    case 1:
        String sql = "ALTER TABLE " + TABLE_SECRET + " ADD COLUMN " + "name_of_column_to_be_added" + " INTEGER";
        db.execSQL(sql);
        break;

    case 2:
        String sql = "SOME_QUERY";
        db.execSQL(sql);
        break;
    }

}

これらのbreakステートメントを削除すると、ループは不要になります
Tash Pemhiwa

しかし、次のケースを通過するには、oldVersionを各ケースでインクリメントする必要があります@TashPemhiwa
Beulah Ana

switchステートメントが中断を必要とする理由は、一度に複数のケースを実行できるためです。これは、ケース条件が満たされていない場合でも当てはまります。@ BeulahAna
Tash Pemhiwa

あなたは休憩を追加し、いくつかのDBは、いくつかの列はすでに、あなたのクエリは、DBのバージョンの損失系列ごとに失敗することができるいくつかのDBのバージョンに変更した場合にブレークを例ALTER TABLEを必要とされていないので、あなたのクエリが失敗することができ、その後、古いまたは新しいバージョンをお持ちの場合
Neerajをシン

2

@jkschneiderの答えは正しいです。ただし、より良いアプローチがあります。

リンクhttps://riggaroo.co.za/android-sqlite-database-use-onupgrade-correctly/で説明されているように、各更新のsqlファイルに必要な変更を書き込みます

from_1_to_2.sql

ALTER TABLE books ADD COLUMN book_rating INTEGER;

from_2_to_3.sql

ALTER TABLE books RENAME TO book_information;

from_3_to_4.sql

ALTER TABLE book_information ADD COLUMN calculated_pages_times_rating INTEGER;
UPDATE book_information SET calculated_pages_times_rating = (book_pages * book_rating) ;

これらの.sqlファイルは、データベースのバージョンに応じてonUpgrade()メソッドで実行されます。

DatabaseHelper.java

public class DatabaseHelper extends SQLiteOpenHelper {

    private static final int DATABASE_VERSION = 4;

    private static final String DATABASE_NAME = "database.db";
    private static final String TAG = DatabaseHelper.class.getName();

    private static DatabaseHelper mInstance = null;
    private final Context context;

    private DatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
        this.context = context;
    }

    public static synchronized DatabaseHelper getInstance(Context ctx) {
        if (mInstance == null) {
            mInstance = new DatabaseHelper(ctx.getApplicationContext());
        }
        return mInstance;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(BookEntry.SQL_CREATE_BOOK_ENTRY_TABLE);
        // The rest of your create scripts go here.

    }


    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        Log.e(TAG, "Updating table from " + oldVersion + " to " + newVersion);
        // You will not need to modify this unless you need to do some android specific things.
        // When upgrading the database, all you need to do is add a file to the assets folder and name it:
        // from_1_to_2.sql with the version that you are upgrading to as the last version.
        try {
            for (int i = oldVersion; i < newVersion; ++i) {
                String migrationName = String.format("from_%d_to_%d.sql", i, (i + 1));
                Log.d(TAG, "Looking for migration file: " + migrationName);
                readAndExecuteSQLScript(db, context, migrationName);
            }
        } catch (Exception exception) {
            Log.e(TAG, "Exception running upgrade script:", exception);
        }

    }

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

    }

    private void readAndExecuteSQLScript(SQLiteDatabase db, Context ctx, String fileName) {
        if (TextUtils.isEmpty(fileName)) {
            Log.d(TAG, "SQL script file name is empty");
            return;
        }

        Log.d(TAG, "Script found. Executing...");
        AssetManager assetManager = ctx.getAssets();
        BufferedReader reader = null;

        try {
            InputStream is = assetManager.open(fileName);
            InputStreamReader isr = new InputStreamReader(is);
            reader = new BufferedReader(isr);
            executeSQLScript(db, reader);
        } catch (IOException e) {
            Log.e(TAG, "IOException:", e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    Log.e(TAG, "IOException:", e);
                }
            }
        }

    }

    private void executeSQLScript(SQLiteDatabase db, BufferedReader reader) throws IOException {
        String line;
        StringBuilder statement = new StringBuilder();
        while ((line = reader.readLine()) != null) {
            statement.append(line);
            statement.append("\n");
            if (line.endsWith(";")) {
                db.execSQL(statement.toString());
                statement = new StringBuilder();
            }
        }
    }
}

サンプルプロジェクトも同じリンクで提供されていますhttps : //github.com/riggaroo/AndroidDatabaseUpgrades


1
私はちょうどここに来て、同じアドバイスを書こうとしていました。私はあなたがすでにそれをやったことを嬉しく思います。リンクした記事は必ず読んでください。これはAndroid SQLiteAssetHelperでもありますがアップグレードに推奨するます。なんとCL(ここでは、スタックオーバーフロー上のSQLiteの専門家)が推奨しています
Suragch、

このコメントは私が探していたものです。SQLスクリプト、+ 1
ブルーウェア

1

データベースバージョンの処理は、アプリケーション開発の非常に重要な部分です。あなたはすでにAppDbHelperクラスを拡張していると思いますSQLiteOpenHelper。それを拡張する場合、実装onCreateonUpgradeメソッドを行う必要があります。

  1. 呼び出されるタイミングonCreateonUpgradeメソッド

    • onCreate アプリが新しくインストールされたときに呼び出されます。
    • onUpgrade アプリが更新されたときに呼び出されます。
  2. データベースのバージョンを整理するクラスメソッドでバージョンを管理します。インターフェース移行の実装を作成します。たとえば、最初のバージョンの作成MigrationV1クラス、2番目のバージョンの作成MigrationV1ToV2(これらは私の命名規則です)


    public interface Migration {
        void run(SQLiteDatabase db);//create tables, alter tables
    }

移行の例:

public class MigrationV1ToV2 implements Migration{
      public void run(SQLiteDatabase db){
        //create new tables
        //alter existing tables(add column, add/remove constraint)
        //etc.
     }
   }
  1. 移行クラスの使用

onCreateonCreateアプリケーションが新しくインストールされたときに呼び出されるので、すべての移行(データベースバージョンの更新)も実行する必要があります。したがって、onCreate次のようになります。

public void onCreate(SQLiteDatabase db){
        Migration mV1=new MigrationV1();
       //put your first database schema in this class
        mV1.run(db);
        Migration mV1ToV2=new MigrationV1ToV2();
        mV1ToV2.run(db);
        //other migration if any
  }

onUpgrade:このメソッドは、アプリケーションが既にインストールされ、新しいアプリケーションバージョンに更新されたときに呼び出されます。アプリケーションにデータベースの変更が含まれている場合は、すべてのデータベースの変更を新しいMigrationクラスに入れ、データベースのバージョンを増やします。

たとえば、ユーザーがデータベースバージョン1のアプリケーションをインストールし、データベースバージョンが2に更新されたとします(すべてのスキーマ更新はに保持されますMigrationV1ToV2)。アプリケーションがアップグレードされたら、次のMigrationV1ToV2ようにデータベーススキーマの変更を適用してデータベースをアップグレードする必要があります。

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    if (oldVersion < 2) {
        //means old version is 1
        Migration migration = new MigrationV1ToV2();
        migration.run(db);
    }
    if (oldVersion < 3) {
        //means old version is 2
    }
}

注:すべてのアップグレード( onUpgradeデータベーススキーマへの)は、onCreate

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.