Android 常见的greenDao升级方式

greenDao 升级方式

默认的升级方案是会删除表后在创建

//默认实现
public static class DevOpenHelper extends OpenHelper {
        public DevOpenHelper(Context context, String name) {
            super(context, name);
        }

        public DevOpenHelper(Context context, String name, CursorFactory factory) {
            super(context, name, factory);
        }

        @Override
        public void onUpgrade(Database db, int oldVersion, int newVersion) {
            Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables");
            dropAllTables(db, true); //删除表
            onCreate(db);   //重新新建
        }
    }

为了保留就数据,所以要复写onUpgrade方法。大概有两种方式

  • 执行Sql语句方式
  • 创建临时表升级后在拷贝数据方式

执行Sql语句的方式

核心很简单,就是升级触发回调时 通过判断数据库的升级版本号执行对于的sql 注入字段,或表

例如:为LoginInfoResult 增加一个Level 等级字段

1.对应的实体类增加 Level字段 并编译生成

@Entity
public class LoginInfoResult implements Cloneable {
    ...
     private String Level;   
    ...
}

2.修改版本号并且执行sql语句进行升级

greendao {
        schemaVersion 2
    }

在application初始化时复写onUpgrade方法

    // do this once, for example in your Application class
        val helper = object: DaoMaster.DevOpenHelper(this, "wecut-db"){
            override fun onUpgrade(db: Database?, oldVersion: Int, newVersion: Int) {
                //注释默认的实现
                //super.onUpgrade(db, oldVersion, newVersion)
                val startVersion = oldVersion + 1
                //for循环避免有旧版本升级上来有未执行到sql
               for (i in startVersion..newVersion) {
                when(i){
                //当前新版本如果是2的时候增加一个Level字段
                    2->{
                       val sql =  "ALTER TABLE "+LoginInfoResultDao.TABLENAME+" ADD column Level TEXT "
                        db?.execSQL(sql)
                    }
                    3->{.其他版本的sql.}
                    4->{.其他版本的sql.}
                  }
                }
            }
        }

3.导出数据库进行查看结果

Android 常见的greenDao升级方式_第1张图片

创建临时表升级后在拷贝数据方式

思路

  1. 创建临时表 TABLE_TMP.拷贝原来的数据到临时表里面
  2. 调用默认到方法,删除原来到表数据,并根据新增到字段创建新的表
  3. 从临时表读取数据,一一拷贝到新创建到表当中

例如: 在LoginInfoResult增加 Level2 字段

1.对应的实体类增加 Level2字段 并编译生成

@Entity
public class LoginInfoResult implements Cloneable {
    ...
     private String Level2;   
    ...
}

2.修改版本号并且执行sql语句进行升级

greendao {
        schemaVersion 2
    }

在application初始化时复写onUpgrade方法

 val helper = object: DaoMaster.DevOpenHelper(this, "wecut-db"){
            override fun onUpgrade(db: Database?, oldVersion: Int, newVersion: Int) {
                //super.onUpgrade(db, oldVersion, newVersion)
                if(db!=null){
                    if(oldVersion<newVersion){
                    
                        //传入要备份的DAO 创建临时表
                        GreenDaoCompatibleUpdateHelper.generateTempTables(db,
                                LoginInfoResultDao::class.java,
                                VipPayInfoDao::class.java)
                                
                        //调用默认的方式删除和创建新的表格
                        super.onUpgrade(db, oldVersion, newVersion)
                        
                        //传入DAO 还原数据
                        GreenDaoCompatibleUpdateHelper.restoreData(db,
                                LoginInfoResultDao::class.java,
                                VipPayInfoDao::class.java)
                    }
                }else{
                   //删除和创建新的表格
                    DaoMaster.dropAllTables(db, true)
                    DaoMaster.createAllTables(db, false)
                }
            }
        }

测试结果
Android 常见的greenDao升级方式_第2张图片

GreenDaoCompatibleUpdateHelper 具体实现类

/**
 * @author: Lai
 * @createDate: 2019-12-13 15:28
 * @description:
 */
class GreenDaoCompatibleUpdateHelper {

    companion object{
        //系统表
        private val SQLITE_MASTER = "sqlite_master"
        //系统临时表
        private val SQLITE_TEMP_MASTER = "sqlite_temp_master"
        
        /**
         * 生成临时表
         * @param sqLiteDatabase Database
         * @param clazz 要备份的DAO
         */
         fun generateTempTables(sqLiteDatabase: Database,
                                       vararg clazz: Class<out AbstractDao<*, *>>) {
            clazz.forEach {
                val daoConfig = DaoConfig(sqLiteDatabase, it)
                //获取当前数据库名称
                val tableName = daoConfig.tablename
                try {
                 //如果表格存在
                    if (isTableExists(sqLiteDatabase, false, tableName)) {
                        //如果存在临时表则删除
                        val tempTableName = tableName + "_TEMP"
                        val dropTableStringBuilder = StringBuilder()
                        dropTableStringBuilder.append("DROP TABLE IF EXISTS ").append(tempTableName).append(";")
                        sqLiteDatabase.execSQL(dropTableStringBuilder.toString())

                        //tableName的数据copy一份都临时表
                        val insertTableStringBuilder = StringBuilder()
                        insertTableStringBuilder.append("CREATE TEMPORARY TABLE ").append(tempTableName)
                        insertTableStringBuilder.append(" AS SELECT * FROM ").append(tableName).append(";")
                        sqLiteDatabase.execSQL(insertTableStringBuilder.toString())
                    }
                } catch (e: java.lang.Exception) {
                    e.printStackTrace()
                }
            }
        }

        /**
         * 还原数据
         *  * @param sqLiteDatabase Database
         * @param clazz 要还原的DAO
         */
         fun restoreData(sqLiteDatabase: Database,
                                vararg clazz: Class<out AbstractDao<*, *>>) {
            clazz.forEach {
                val daoConfig = DaoConfig(sqLiteDatabase, it)
                val tableName = daoConfig.tablename
                val tempTableName = daoConfig.tablename + "_TEMP"

                if (isTableExists(sqLiteDatabase, true, tempTableName)) {
                    try {
                        // 获取对于的表格字段数据
                        val newTableInfos = TableInfo.getTableInfo(sqLiteDatabase, tableName)
                        val tempTableInfos = TableInfo.getTableInfo(sqLiteDatabase, tempTableName)
                        
                        val selectColumns = ArrayList<String>()
                        val intoColumns = ArrayList<String>()

                        for (tableInfo in tempTableInfos) {
                            if (newTableInfos.contains(tableInfo)) {
                                val column = "`" + tableInfo.name + "`"
                                intoColumns.add(column)
                                selectColumns.add(column)
                            }
                        }

                        // NOT NULL columns list
                        for (tableInfo in newTableInfos) {
                            if (tableInfo.notnull && !tempTableInfos.contains(tableInfo)) {
                                val column = '`'.toString() + tableInfo.name + '`'.toString()
                                intoColumns.add(column)

                                val value = if (tableInfo.dfltValue != null) {
                                    "'" + tableInfo.dfltValue + "' AS "
                                } else {
                                    "'' AS "
                                }
                                selectColumns.add(value + column)
                            }
                        }

                    //拼接好后执行sql还原数据
                        if (intoColumns.size != 0) {
                            val insertTableStringBuilder = StringBuilder()
                            insertTableStringBuilder.append("REPLACE INTO ").append(tableName).append(" (")
                            insertTableStringBuilder.append(TextUtils.join(",", intoColumns))
                            insertTableStringBuilder.append(") SELECT ")
                            insertTableStringBuilder.append(TextUtils.join(",", selectColumns))
                            insertTableStringBuilder.append(" FROM ").append(tempTableName).append(";")
                            val sql = insertTableStringBuilder.toString()
                            sqLiteDatabase.execSQL(sql)
                        }

                        //删除临时表的数据
                        val dropTableStringBuilder = StringBuilder()
                        dropTableStringBuilder.append("DROP TABLE ").append(tempTableName)
                        sqLiteDatabase.execSQL(dropTableStringBuilder.toString())
                    } catch (e: java.lang.Exception) {
                        e.printStackTrace()
                    }
                }
            }
        }


        private fun isTableExists(db: Database?, isTemp: Boolean, tableName: String): Boolean {
            if (db == null || TextUtils.isEmpty(tableName)) {
                return false
            }
            val dbName = if (isTemp) SQLITE_TEMP_MASTER else SQLITE_MASTER
            val sql = "SELECT COUNT(*) FROM $dbName WHERE type = ? AND name = ?"
            var cursor: Cursor? = null
            var count = 0
            try {
                cursor = db.rawQuery(sql, arrayOf("table", tableName))
                if (cursor == null || !cursor.moveToFirst()) {
                    return false
                }
                count = cursor.getInt(0)
            } catch (e: Exception) {
                e.printStackTrace()
            } finally {
                cursor?.close()
            }
            return count > 0
        }
    }


    class TableInfo {
        var cid: Int = 0
        var name: String? = null
        var type: String? = null
        var notnull: Boolean = false
        var dfltValue: String? = null
        var pk: Boolean = false

        override fun equals(o: Any?): Boolean {
            return this === o || (o != null
                    && javaClass == o.javaClass
                    && name == (o as TableInfo).name)
        }

        override fun toString(): String {
            return "TableInfo{" +
                    "cid=" + cid +
                    ", name='" + name + '\''.toString() +
                    ", type='" + type + '\''.toString() +
                    ", notnull=" + notnull +
                    ", dfltValue='" + dfltValue + '\''.toString() +
                    ", pk=" + pk +
                    '}'.toString()
        }

        companion object {
        
            fun getTableInfo(db: Database, tableName: String): List<TableInfo> {
                val sql = "PRAGMA table_info($tableName)"
                val cursor = db.rawQuery(sql, null) ?: return ArrayList()
                var tableInfo: TableInfo
                val tableInfos = ArrayList<TableInfo>()
                while (cursor.moveToNext()) {
                    tableInfo = TableInfo()
                    tableInfo.cid = cursor.getInt(0)
                    tableInfo.name = cursor.getString(1)
                    tableInfo.type = cursor.getString(2)
                    tableInfo.notnull = cursor.getInt(3) == 1
                    tableInfo.dfltValue = cursor.getString(4)
                    tableInfo.pk = cursor.getInt(5) == 1
                    tableInfos.add(tableInfo)
                    // printLog(tableName + ":" + tableInfo);
                }
                cursor.close()
                return tableInfos
            }
        }     
    }

}

你可能感兴趣的:(greenDao)