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.導出數據庫進行查看結果

在這裏插入圖片描述

創建臨時表升級後在拷貝數據方式

思路

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

測試結果
在這裏插入圖片描述

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
            }
        }     
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章