Android資料庫表結構自動升級
Android App開發如果涉及過資料庫的朋友們肯定會碰到資料庫升級的工作,Android官方的建議辦法是override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
並在其中通過資料庫版本比較寫SQL增加表字段、建立新表等操作來達到資料庫升級的功能,思路非常OK,但是專案做久了發現這塊程式碼變超級龐大,仔細一看全是流水賬程式碼,自己專案中曾經也是如此,時間久了程式碼量十分恐怖,關鍵的是無法通過重構減少程式碼量,類似如下:

db_upgrade.png
其實,之前寫過一個輕量級的SQLite ORM,已經做到資料庫自動建立,通過以面向物件方式進行增刪改查,非常缺少自動升級這個功能。
通過查閱相關資料,得知sqlite資料庫裡預設會生成兩個表分別是:sqlite_sequence和sqlite_master,今天看的是sqlite_master,裡面存放了每張表結構(建立表的SQL):

sqlite_master.png
所以,思路就很簡單了,同過檢索此表可以知道新建的表是否在其中存在:
- 不存表在則建立新表;
- 存在表再檢查所有欄位是否存在,不存在則加欄位(資料庫升級的原則就是隻增不減);
所以,寫兩個工具方法即可:
static boolean isTableExist(SQLiteDatabase db, String tableName) { Cursor cursor = null; try { cursor = db.rawQuery("SELECT count(*) FROM sqlite_master WHERE type='table' AND name=?", new String[]{tableName}); boolean hasNext = cursor.moveToNext(); return hasNext && cursor.getInt(0) > 0; } finally { if (cursor != null) { cursor.close(); } } } static boolean isColumnExist(SQLiteDatabase db, String tableName, String columnName) { Cursor cursor = null; try { cursor = db.rawQuery("SELECT count(*) FROM sqlite_master WHERE tbl_name = ? AND (sql LIKE ? OR sql LIKE ?);", new String[]{tableName, "%(" + columnName + "%", "%, " + columnName + " %"}); boolean hasNext = cursor.moveToNext(); return hasNext && cursor.getInt(0) > 0; } finally { if (cursor != null) { cursor.close(); } } }
如何運用2個方法自動升級呢:
@Override public final void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { autoMigrate(db, mTableClasses); } private void autoMigrate(SQLiteDatabase db, List<Class<? extends Entity>> tableClasses) { for (Class<? extends Entity> clazz : tableClasses) { String tableName = ReflectTools.getTableName(clazz); boolean exist = ReflectTools.isTableExist(db, tableName); if (exist) { Field[] fields = ReflectTools.getClassFields(clazz); for (Field field : fields) { Column column = field.getAnnotation(Column.class); if (column == null) { continue; } String columnName = !TextUtils.isEmpty(column.name()) ? column.name() : field.getName(); String dataType = ReflectTools.getDataTypeByField(field); boolean columnExist = ReflectTools.isColumnExist(db, tableName, columnName); if (!columnExist) { db.execSQL("ALTER TABLE " + tableName + " ADD " + columnName + " " + dataType); } } } else { db.execSQL(SQLBuilder.buildCreateSQL(clazz).getSql()); } } }
可能你們已經注意到這裡有幾個外來方法和變數,它們來自於上面所說的輕量級SQLite ORM,還有請注意onUpgrade()是加了final修飾的,意味著子類無需手動升級了。
所以,關於資料庫升級,你所需要做的事情就是該建立表物件的就建立表物件,該加欄位的就加欄位,最後別忘記把資料庫版本號升級下就好了,因為只有當Android檢測出你的資料庫版本好變了才會走進onUpgrade()。