Android數(shù)據(jù)庫表結(jié)構(gòu)自動(dòng)升級(jí)

Android App開發(fā)如果涉及過數(shù)據(jù)庫的朋友們肯定會(huì)碰到數(shù)據(jù)庫升級(jí)的工作,Android官方的建議辦法是override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)并在其中通過數(shù)據(jù)庫版本比較寫SQL增加表字段、創(chuàng)建新表等操作來達(dá)到數(shù)據(jù)庫升級(jí)的功能,思路非常OK,但是項(xiàng)目做久了發(fā)現(xiàn)這塊代碼變超級(jí)龐大,仔細(xì)一看全是流水賬代碼,自己項(xiàng)目中曾經(jīng)也是如此,時(shí)間久了代碼量十分恐怖,關(guān)鍵的是無法通過重構(gòu)減少代碼量,類似如下:

db_upgrade.png

其實(shí),之前寫過一個(gè)輕量級(jí)的SQLite ORM,已經(jīng)做到數(shù)據(jù)庫自動(dòng)創(chuàng)建,通過以面向?qū)ο蠓绞竭M(jìn)行增刪改查,非常缺少自動(dòng)升級(jí)這個(gè)功能。
通過查閱相關(guān)資料,得知sqlite數(shù)據(jù)庫里默認(rèn)會(huì)生成兩個(gè)表分別是:sqlite_sequence和sqlite_master,今天看的是sqlite_master,里面存放了每張表結(jié)構(gòu)(創(chuàng)建表的SQL):

sqlite_master.png

所以,思路就很簡(jiǎn)單了,同過檢索此表可以知道新建的表是否在其中存在:

  • 不存表在則創(chuàng)建新表;
  • 存在表再檢查所有字段是否存在,不存在則加字段(數(shù)據(jù)庫升級(jí)的原則就是只增不減);

所以,寫兩個(gè)工具方法即可:

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();
        }
    }
}

如何運(yùn)用2個(gè)方法自動(dòng)升級(jí)呢:

@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)注意到這里有幾個(gè)外來方法和變量,它們來自于上面所說的輕量級(jí)SQLite ORM,還有請(qǐng)注意onUpgrade()是加了final修飾的,意味著子類無需手動(dòng)升級(jí)了。

所以,關(guān)于數(shù)據(jù)庫升級(jí),你所需要做的事情就是該創(chuàng)建表對(duì)象的就創(chuàng)建表對(duì)象,該加字段的就加字段,最后別忘記把數(shù)據(jù)庫版本號(hào)升級(jí)下就好了,因?yàn)橹挥挟?dāng)Android檢測(cè)出你的數(shù)據(jù)庫版本好變了才會(huì)走進(jìn)onUpgrade()。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容