2012年10月30日火曜日

[Android]データベースをアップグレードする時

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を開き、上記のクエリーを発行すれば良さそうです。
データベースの構造を変えるのは大変ですが、こういう仕組みが用意されているのは有り難いですね。

0 件のコメント:

コメントを投稿