データベースをアップグレードする時
SQLiteのデータベースを使用する際はSQLiteOpenHelperクラスを使います。一般的には下のようなコードになると思います。
public class DatabaseHelper extends SQLiteOpenHelper { public DatabaseHelper(Context context) { super(context, "test.db", null, 1); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + test_table + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY," + "name TEXT" + ");" ); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }
使用法はコンストラクタからインスタンスを作成し、getReadableDatabaseかgetWritableDatabaseを呼び出して参照や更新のクエリを発行するSQLiteDatabaseクラスを取得することになります。
onCreateは初めてデータベースにアクセスする際に呼ばれるので、ここでデータベースの初期化を行います。onUpgradeはバージョンアップの際に呼び出されますので、ここでALTER TABLEなどのSQLを使って対応することになります。oldVersionとnewVersionが渡ってくるので、バージョンが1から2になった場合、2から3になった時などで分岐させられます。
バージョンアップの方法
上で作成したデータベースに、機能追加で新しいテーブルを追加することになった場合、以下のようにします。
- コンストラクタで呼び出す最後の引数が、新しいデータベースのバージョンになります。前回1だったので、今回は2にします。
- onCreateで行う初期化は最新バージョンのものにします。古いデータベースが作成されている場合はonCreateは実行されません。
- onUpgradeに、古いデータベースが作成されている場合のバージョンアップコードを書きます。
public class DatabaseHelper extends SQLiteOpenHelper { public DatabaseHelper(Context context) { super(context, "test.db", null, 2); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + test_table + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY," + "name TEXT" + ");" ); db.execSQL("CREATE TABLE " + test_table2 + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY," + "name TEXT" + ");" ); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if( oldVersion == 1 && newVersion == 2 ){ db.execSQL("CREATE TABLE " + test_table2 + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY," + "name TEXT" + ");" ); } } }
これで、新しいバージョンを新規にインストールした時と、古いバージョンからバージョンアップした時のテーブル構成を同じにすることが出来ます。
内部の処理
実際にSQLiteOpenHelperクラスはどんな処理をしているのか、SDKのソースを覗いてみました。onCreateやonUpgradeは、getReadableDatabaseやgetWritableDatabaseの内部で実行されていました。その中のコードを引用します。
SQLiteOpenHelper.java
mIsInitializing = true; if (mName == null) { db = SQLiteDatabase.create(null); } else { db = mContext.openOrCreateDatabase(mName, 0, mFactory); } int version = db.getVersion(); if (version != mNewVersion) { db.beginTransaction(); try { if (version == 0) { onCreate(db); } else { onUpgrade(db, version, mNewVersion); } db.setVersion(mNewVersion); db.setTransactionSuccessful(); } finally { db.endTransaction(); } }
内部の処理では、最初にバージョン0として空のデータベースを作成しonCreateを呼び出しています。データベースが存在していてバージョンが変わっていればonUpgradeを呼び出していますね。どちらもトランザクションで囲まれているので、onCreateやonUpgradeの中でトランザクションを使用する必要は無いみたいです。
ところで、現在のデータベースのバージョンはどこに保存されているのか、SQLiteDatabase.getVersion()とsetVersion()のソースを見てみます。
SQLiteDatabase.java
public int getVersion() { SQLiteStatement prog = null; lock(); try { prog = new SQLiteStatement(this, "PRAGMA user_version;"); long version = prog.simpleQueryForLong(); return (int) version; } finally { if (prog != null) prog.close(); unlock(); } } public void setVersion(int version) { execSQL("PRAGMA user_version = " + version); }
テーブルでは無く、SQLiteのPRAGMAのuser_versionで管理していました。PRAGMAって知らなかったのですが、SQLite3での環境変数のようなものみたいです。http://www.sqlite.org/pragma.html
なので、データベースのバージョンが知りたい場合は、AndroidのSDKのshellからSQLiteでDBを開き、上記のクエリーを発行すれば良さそうです。
データベースの構造を変えるのは大変ですが、こういう仕組みが用意されているのは有り難いですね。