greenDAO自動化升級探索
前言
好像有好久沒寫博客的樣子~ 行,那今天就再找個理由吹一波。 前段時間研究了一下greendao數據庫升級模塊,發現了一些存在的一些問題痛點,特拿來晾曬一下,以防發黴。
問題現狀
話說爲什麼要做數據庫自動升級這塊的探索呢,主要有以下幾點原因:
- 現有的數據庫升級方式過於繁瑣,每個版本都需要進行一次手動升級,每次升級都要寫一大推if else判斷新舊數據庫版本,一不小心就容易出錯。
- 出現跨版本升級數據庫的時候,偶爾會出現數據庫字段丟失的情況,造成一些用戶閃退現象。
- 主要還是人懶,不想每次都寫一大堆重複的代碼
思考
話說有沒有一種方式能夠比較優雅地解決這個問題呢?一波搜索後,發現很多解決方案基本都是類似的,分爲兩類:
第一類:根據當前版本依次遞歸的常規升級方式,即每個新版發佈都在對應的版本號下面加入新增的表或者字段。這種傳統的升級方式,顯得不夠“自動化”,寫起來比較麻煩,而且有時候還容易遺漏掉部分新增字段,造成應用的崩潰問題。
第二類:基本上參考了stackoverflow上面一位大佬的自動化升級方式。他的思路是這樣的:
1.拷貝原有數據表,新建temp表備份數據
2.刪除原有數據表
3.新建現有數據表
4.把temp表備份數據插入到新建的現有表中
5.刪除備份temp表
6.balabalabla...
反正就是一頓操作猛如虎,數據搬過來搬過去,刪完再建、各種反射,看起來很炫酷的樣子。
我就在想,爲什麼就不直接遍歷檢測 缺失表 + 缺失表字段,然後直接插入缺失的表或字段呢?如果可以這樣操作的話,那麼性能方面肯定會有一個顯著的提升,極大的減少了數據庫操作開銷,豈不是看起來很棒棒?
解決方案
這個時候,一個熱乎的方案新鮮出爐了。主要思路還是遍歷數據庫尋找缺失的表和表字段。然後完善對應的表結構。
public final class MigrationHelper {
private static final String TAG = "MigrationHelper";
private static final String SQLITE_MASTER = "sqlite_master";
private static final String SQLITE_TEMP_MASTER = "sqlite_temp_master";
public static void migrate(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
Database database = new StandardDatabase(db);
migrate(database, daoClasses);
}
public static void migrate(Database database, Class<? extends AbstractDao<?, ?>>... daoClasses) {
generateTempTables(database, daoClasses);
for (int i = 0; i < daoClasses.length; i++) {
DaoConfig daoConfig = new DaoConfig(database, daoClasses[i]);
dropTable(database, true, daoConfig);
createTable(database, false, daoConfig);
}
restoreData(database, daoClasses);
}
private static void dropTable(Database database, boolean ifExists, DaoConfig daoConfig) {
String sql = String.format("DROP TABLE %s\"%s\"", ifExists ? "IF EXISTS " : "", daoConfig.tablename);
database.execSQL(sql);
}
private static void generateTempTables(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
for (int i = 0; i < daoClasses.length; i++) {
String tempTableName = null;
DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);
String tableName = daoConfig.tablename;
if (!isTableExists(db, false, tableName)) {
continue;
}
try {
tempTableName = daoConfig.tablename.concat("_TEMP");
StringBuilder dropTableStringBuilder = new StringBuilder();
dropTableStringBuilder.append("DROP TABLE IF EXISTS ").append(tempTableName).append(";");
db.execSQL(dropTableStringBuilder.toString());
StringBuilder insertTableStringBuilder = new StringBuilder();
insertTableStringBuilder.append("CREATE TEMPORARY TABLE ").append(tempTableName);
insertTableStringBuilder.append(" AS SELECT * FROM "