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.導出數據庫進行查看結果
創建臨時表升級後在拷貝數據方式
思路
- 創建臨時表 TABLE_TMP.拷貝原來的數據到臨時表裏面
- 調用默認到方法,刪除原來到表數據,並根據新增到字段創建新的表
- 從臨時表讀取數據,一一拷貝到新創建到表當中
例如: 在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
}
}
}
}