標籤:android 資料庫設計 資料庫升級 onupgrade sqliteopenhelper
資料庫升級的意義
我們在開發Android應用的時候,不可避免地要使用資料庫。而資料庫的結構在第一版的時候定下來,之後發布功能更新,或增加商務邏輯,原來的資料庫結構可能就不適用了。而如果資料庫的結構與之前版本的結構不同,新版本的應用讀取舊資料庫肯定會出問題。解決辦法只有兩種:
1.讓使用者卸載老版本再安裝新的程式;
2.軟體自行更新資料庫結構。
第一種辦法很明顯不具備可操作性,而且使用者一旦卸載軟體,資料就丟失了,這是不能容忍的事情。因此,作為開發人員必須妥善處理資料庫的升級問題。
當然了,有的同學會說,這問題沒意義嘛。我們在設計軟體的時候就把資料庫設計得完備一點就好了,一開始就考慮周全,以後再也不用管升級的事情。這種方法理論上雖然可行,但實際操作起來是非常困難的,除非你在開發定製軟體(例如某些和硬體結合的產品,其硬體發布之後就不再更新或很少更新,這樣的軟體確實沒多大改動)。對於面向市場的應用來說,很可能在立項之初根本不會知道以後會增加哪些功能。這樣,我們終究還是要面對這個問題。
保留資料的升級
現在以一個假想的程式資料庫來談如何保留原有的資料庫進行升級。為直觀起見,這裡在每次軟體版本安裝之後匯出了資料庫檔案,並使用SQLite Studio顯示表結構和內容,過程從略。並且為了代碼的簡潔,省略了一些異常處理。我們假設資料庫有這樣三個版本:
v1:t_user(username, password);
v2:t_user(username, password), t_region(region, code);
v3:t_user(username, password), t_region(region, code, country);
可以看出,第一次升級增加了一張表,而第二次升級修改了表的定義。
建立和升級資料表的基本知識
我們在使用資料庫之前,基本上會自訂一個類繼承自SQLiteOpenHelper。該類的其中一個建構函式形式是這樣的(另一個多出來一個DatabaseErrorHandler):
public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) { this(context, name, factory, version, null); }
這個建構函式裡面的version參數即是我們設定的版本號碼。第一次使用資料庫時傳遞的這個版本將被系統記錄,並調用SQLiteOpenHelper#onCreate()方法進行建表操作。後續傳入的版本如果比這個高,則會調用SQLiteOpenHelper#onUpgrade()方法進行升級。
增加一張表
很明顯,增加新表(或者刪除沒有外鍵的表)的操作代價很小,只需要在onUpgrade()中寫好建表操作和插入初始資料就好了。
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {if (oldVersion==1){db.execSQL("CREATE TABLE t_region(_id integer primary key" + "autoincrement, region varchar, code varchar)");//insert data...}}
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M00/44/FA/wKioL1PjS46A0YztAAEsNiHN2ns849.jpg" title="版本1資料庫結構" alt="wKioL1PjS46A0YztAAEsNiHN2ns849.jpg" />
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M01/44/F5/wKioL1PjQQvCoE7JAAEoZeplm10837.jpg" title="版本2資料庫結構" alt="wKioL1PjQQvCoE7JAAEoZeplm10837.jpg" />
從上面的圖裡可以看到,新版本的資料庫中已經有t_region表了。
修改表定義
SQLite數庫對ALTER TABLE命令支援非常有限,只能在表末尾添加列,不能修改列定義,不能刪除已有的列。那麼如果要修改表呢?我們可以採用暫存資料表的辦法。具體來說有四步:
將現有表重新命名為暫存資料表;
建立新表;
將暫存資料表的資料匯入新表(注意處理修改的列);
刪除暫存資料表。
以例子中的v2升級到v3為例:
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {if (oldVersion==2){db.execSQL("ALTER TABLE t_region RENAME TO t_region_temp");db.execSQL("CREATE TABLE t_region(_id integer primary key" + "autoincrement, region varchar, code varchar, " + "country varchar)");db.execSQL("insert into t_region(_id, region, code, country) " + "select _id, region, code, \"CHINA\" from t_region_temp");db.execSQL("DROP TABLE t_region_temp");}}
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M02/44/F7/wKioL1PjRYui_CqZAAExr2IApJw742.jpg" title="版本2資料" alt="wKioL1PjRYui_CqZAAExr2IApJw742.jpg" />
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M02/44/F7/wKioL1PjRYvzqrd-AAElIqNNLCI543.jpg" title="版本3資料" alt="wKioL1PjRYvzqrd-AAElIqNNLCI543.jpg" />
需要注意:
跨越版本的升級
處理好了單個版本的升級,還有一個更加棘手的問題:如果應用程式發布了多個版本,以致出現了三個以上資料庫版本, 如何確保所有的使用者升級應用後資料庫都能用呢?有兩種方式:
方式一:確定相鄰版本的差別,從版本1開始依次迭代更新,先執行v1到v2,再v2到v3……
方式二:為每個版本確定與現在資料庫的差別,為每個case撰寫專門的升級代碼。
方式一的優點是每次更新資料庫的時候只需要在onUpgrade方法的末尾加一段從上個版本升級到新版本的代碼,易於理解和維護,缺點是當版本變多之後,多次迭代升級可能需要花費不少時間,增加使用者等待;
方式二的優點則是可以保證每個版本的使用者都可以在消耗最少的時間升級到最新的資料庫而無需做無用的資料多次轉存,缺點是強迫開發人員記憶所有版本資料庫的完整結構,且每次升級時onUpgrade方法都必須全部重寫。
以上簡單分析了兩種方案的優缺點,它們可以說在花費時間上是剛好相反的,至於如何取捨,可能還需要結合具體情況分析。
本文出自 “飛翔的貓咪” 部落格,請務必保留此出處http://flyingcat2013.blog.51cto.com/7061638/1537074