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 "