軟件迭代過程當中,業務不斷更新,也要求軟件持續更新。相應地,數據庫更新升級也是不可避免的一個環節。Android做爲客戶端應用,數據庫升級相對於服務端來講會麻煩一些。常見的升級方式有:git
1.刪除舊錶和數據,建立新表。優勢是簡單方便,缺點是丟失了舊數據。適用於應用數據依賴度低的狀況。github
2.在代碼中兼容處理各版本數據庫,建立新表,遷移舊數據到新表。優勢是保留了舊數據,缺點是須要處理兼容個版本數據庫差別,比較麻煩。若是經過代碼來記錄維護版本差別,會致使代碼臃腫且極易出錯。sql
本文介紹一種簡單無縫的數據庫升級方案,也是屬於上述的第二種方式,可是簡單、高效地處理了數據庫版本兼容。代碼已實如今DBFramework中,這是一個輕量的數據庫框架,簡單、規範、高效,可以很好的處理表之間的繼承關係,能夠爲你節省不少開發工做。數據庫
感興趣的朋友能夠了解一下。框架
數據庫升級,實際上是對其中的表進行升級。要更新表而且保留數據,這就必需要知道新表和舊錶之間的差別。若是咱們靠開發人員來記錄各版本之間表的差別,無疑是一個困難和繁雜的工做。那麼當咱們進行數據庫的時候,客戶端應用有沒有辦法知道這些差別,知道有哪些表須要更新呢?答案是確定的。在Sqlite中有一張叫作sqlite_master的系統表,其中記錄了sqlite中的全部表信息,以下:ide
能夠看到,其中除了表名,還有表建立語句。有了建立語句,咱們就能解析出表的字段信息,而後對比新表和舊錶的字段,有差別則說明表須要更新,反之則不須要更新。以下代碼用來獲取現有的表信息:spa
/** * Get old tables. * @return A map contains old tables which takes table name as key. */ public Map<String, Table> getOldTables() { final String table = "sqlite_master"; final String[] columns = {"name", "sql"}; Cursor cursor = mDB.query(table, columns, "type='table'", null, null, null, null); HashMap<String, Table> ret = null; if (cursor.getCount() > 0) { ret = new HashMap<String, Table>(cursor.getCount()); Table tmp; while (cursor.moveToNext()) { tmp = new Table(cursor.getString(0), cursor.getString(1)); ret.put(tmp.Name, tmp); } } cursor.close(); return ret == null ? Collections.EMPTY_MAP : ret; }
Table是一個記錄表信息的類,拿到現有表信息,就能夠和新的表信息進行對比,主要對比表字段的變化:code
public class Table { ... public boolean equalsColumns(Table table) { String myColumns = getColumnsStatement(CreateSql).toUpperCase(); String columns = getColumnsStatement(table.CreateSql).toUpperCase(); return (myColumns.equals(columns)); } ... }
若是無變化則跳過,有變化則更新,更新過程爲:sqlite
1.將現有表以別名命名blog
2.建立新表
3.遷移數據
4.刪除現有表
以下代碼所示:
@Override protected void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion, List<Table> tableList) { Map<String, Table> oldMap = getOldTables(); Iterator<Table> iterator = tableList.iterator(); Table table; Table oldTable; String tempTable; while (iterator.hasNext()) { table = iterator.next(); if ((oldTable = oldMap.get(table.Name)) == null) { //New table, create directly. db.execSQL(table.CreateSql); Log.i(TAG, "Table " + table.Name + " is a new table. Create it directly"); continue; } //Remove hit table. oldMap.remove(table.Name); if (oldTable.equalsColumns(table)) { //Table not change. Log.i(TAG, "Table " + table.Name + " doesn't need update"); continue; } tempTable = table.Name + TEMP_SUFFIX; Log.i(TAG, "Update table: " + table.Name); //Table changed. //Alter old table as temp. alterTableName(oldTable.Name, tempTable); //Create new table. db.execSQL(table.CreateSql); //Copy data. copyData(getCommonColumn(oldTable.getColumns(), table.getColumns()), tempTable, table.Name); //Delete old table deleteTable(tempTable); } //Delete obsolete tables not hit. deleteObsoleteTables(oldMap.values()); }
更多細節可查看源碼:DBFramework。
這種方案簡單方便,並且幾乎一勞永逸。推薦小夥伴們使用,若是有更好的方式或者寶貴意見,歡迎交流。