App版本更新時對SQLite數據庫升級或者降級遇到的問題

轉自博客:http://www.pedant.cn/2014/08/01/sqliteopenhelper-onupgrade-ondowngrade-handle/

SQLite是Android內置的一個很小的關係型數據庫。SQLiteOpenHelper是一個用來輔助管理數據庫創建和版本升級問題的抽象類。我們可以繼承這個抽象類,實現它的一些方法來對數據庫進行自定義操作。下面兩個方法必須重寫:

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

另外SQLiteOpenHelper子類在構造實例時必須指定當前數據庫的名稱(name)、版本號(version)。而這裏名稱就決定了數據庫存儲時的文件名稱,而這裏的版本號與App在AndroidMainfest.xml定義的versionCode沒有絕對關聯。也就是在App更新時如果數據庫的數據結構沒有發生變化那麼數據庫的版本號則不用增加。

onCreate:調用時機是用戶首次安裝應用後啓動,或是清除App數據庫文件後啓動。這時可以在這個函數中完成初始的數據表的創建。

onUpgrade:調用時機是用戶在做應用更新,覆蓋安裝後啓動,如果新版本中數據庫版本號要比舊版本中的數據庫版本號高則會調用。這時可以在這個函數完成數據庫版本升級帶來的舊版本的兼容問題,以及數據遷移問題。

還有一個一般情況下不需要重寫,但在應用出現逆向降級(如應用由版本號4降級安裝版本號爲3的包)時必須重寫的方法onDowngrade,如果應用降級覆蓋安裝時沒有重寫該方法則會崩潰。

在數據庫版本升級時, 我們可能會遇到這樣一些情況:

  • 需要擴展一個表的字段
  • 刪除掉原來表上某個冗餘的字段
  • 新建一個表

而處理上面這些問題都要在不損害舊數據庫歷史數據的前提下完成。這裏我們假設用戶手機上之前安裝的是數據庫版本爲1的包,升級安裝的是數據庫版本號爲2的包。這時我們要在數據庫版本爲2的包在去處理這些升級邏輯。

首先是擴展一個表的字段在onUpgrade中的實現爲:

MyDBHelper.java
1
2
3
4
5
6
7
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//舊數據庫版本爲1,才爲表pedant添加一個student_name字段
if(oldVersion < 2) {
db.execSQL("ALTER TABLE pedant ADD COLUMN student_name text");
}
}

SQLite對ALTER TABLE的支持是有限的,你可以在一個存在表上添加一個字段到末尾,或者是改變表的名稱。但如果你想做更復雜的操作,比如刪除一個表已有的字段,就要重新創建這個表並完成數據遷移,而不能使用DROP COLUMN這樣方便的命令了。詳見SQLite Frequently Questions

比如表pedant原來有三個字段a、b、c,現在想刪除c字段,那麼在onUpgrade中寫法如下:

MyDBHelper.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//舊數據庫版本爲1,刪除表pedant的c字段
if(oldVersion < 2) {
db.beginTransaction();
try {
db.execSQL("CREATE TEMPORARY TABLE pe_backup (a, b);");
db.execSQL("INSERT INTO pe_backup SELECT a, b FROM pedant;");
db.execSQL("DROP TABLE pedant;");
db.execSQL("CREATE TABLE pedant(a text, b text);");
db.execSQL("INSERT INTO pedant SELECT a, b FROM pe_backup;");
db.execSQL("DROP TABLE pe_backup;");
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
}

這樣就既完成了對c字段的刪除也保留了原來表上的數據。

最後一種情況最簡單直接執行CREATE語句就要可以了。

MyDBHelper.java
1
2
3
4
5
6
7
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//舊數據庫版本爲1,創建新表newtb
if(oldVersion < 2) {
db.execSQL("CREATE TABLE newtb(a text, b text);");
}
}

數據庫在做升級時我們能明確地知道當前我們要對各舊錶進行什麼樣的操作來兼容新版本。但如果在數據庫降級時,情況就不一樣了,針對我們開發新版本2時, 我們不能明確地知道以後的新版本比如版本3、4的數據庫結構走向是怎樣的。比如以後用戶從版本3回退到我們正在開發的版本2,由於我們開發當時不能預知版本3的表結構,不知版本3的數據表能否兼容到版本2(假如版本3升級時刪除了一個版本2一直在用的表字段,這時回退數據結構可能就不兼容了),那麼我們在開發版本2時最穩妥的做法是重寫onDowngrade時把所有當前版本將用到的表全部重建,即降級時扔掉以前全部的數據

MyDBHelper.java
1
2
3
4
5
6
7
8
9
10
// 因爲我們無法預知未來版本的表結構,向下兼容時最穩妥的方法就是將該版本自己需要的表重構一次
@Override
public void onDowngrade (SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS t1;");
db.execSQL("DROP TABLE IF EXISTS t2;");
db.execSQL("DROP TABLE IF EXISTS t3;");
db.execSQL("DROP TABLE IF EXISTS t4;");
....
onCreate(db); // 建表
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章