Android App開發(fā)如果涉及過數(shù)據(jù)庫的朋友們肯定會碰到數(shù)據(jù)庫升級的工作,Android官方的建議辦法是override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
并在其中通過數(shù)據(jù)庫版本比較寫SQL增加表字段就轧、創(chuàng)建新表等操作來達到數(shù)據(jù)庫升級的功能馁龟,思路非常OK是尔,但是項目做久了發(fā)現(xiàn)這塊代碼變超級龐大幻枉,仔細一看全是流水賬代碼牲芋,自己項目中曾經(jīng)也是如此呜达,時間久了代碼量十分恐怖,關(guān)鍵的是無法通過重構(gòu)減少代碼量滑潘,類似如下:
其實垢乙,之前寫過一個輕量級的SQLite ORM,已經(jīng)做到數(shù)據(jù)庫自動創(chuàng)建语卤,通過以面向?qū)ο蠓绞竭M行增刪改查追逮,非常缺少自動升級這個功能。
通過查閱相關(guān)資料粹舵,得知sqlite數(shù)據(jù)庫里默認會生成兩個表分別是:sqlite_sequence和sqlite_master钮孵,今天看的是sqlite_master,里面存放了每張表結(jié)構(gòu)(創(chuàng)建表的SQL):
所以眼滤,思路就很簡單了巴席,同過檢索此表可以知道新建的表是否在其中存在:
- 不存表在則創(chuàng)建新表;
- 存在表再檢查所有字段是否存在诅需,不存在則加字段(數(shù)據(jù)庫升級的原則就是只增不減)漾唉;
所以,寫兩個工具方法即可:
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());
}
}
}
可能你們已經(jīng)注意到這里有幾個外來方法和變量诱担,它們來自于上面所說的輕量級SQLite ORM毡证,還有請注意onUpgrade()是加了final修飾的电爹,意味著子類無需手動升級了蔫仙。
所以,關(guān)于數(shù)據(jù)庫升級丐箩,你所需要做的事情就是該創(chuàng)建表對象的就創(chuàng)建表對象摇邦,該加字段的就加字段,最后別忘記把數(shù)據(jù)庫版本號升級下就好了屎勘,因為只有當(dāng)Android檢測出你的數(shù)據(jù)庫版本好變了才會走進onUpgrade()施籍。