通过从资产中重新创建来处理房间数据库更新,除了我需要保留的几列之外 - Android 开发
Handling Room Database update by reacreating from asset except for few columns I need to persist - Android Development
我的数据库有游戏配置数据和动态用户数据。每次我更新应用程序时,我都会更改很多配置数据(更新记录并添加新记录),并且很容易删除数据库并从资产中重新创建。除了需要保留且不受游戏配置更新影响的用户数据外,所有内容均已更新。
那么我想做些什么来避免冗长的迁移代码:
- 从 Db 中读取用户数据(2 列)将它们临时存储在某个地方
- 删除整个数据库并从我更新的资产中重新创建
- 将用户数据写回这两列。
从技术的角度来看,我总是觉得很难做到这一点,我想知道是否有人知道这是否可能或以前做过?
如果只有两列,我建议您将其从数据库中删除并存储在其他地方。例如在 SharedPreferences 中。或者这不是你能做的?
我遗漏了您的实施细节。
如果您正在使用 Room,这将是试用 Destructive migrations 的绝佳机会。构建数据库时,只需启用破坏性迁移,更新数据库版本时会自动完成工作。
Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
.createFromAsset("database/myapp.db")
.fallbackToDestructiveMigration()
.build()
and it easy to just delete the database and recreate from asset.
而不是删除数据库重命名它,因此它仍然可用。
使用prePackedDatabase
回调应用重命名版本的数据(调用回调时预打包的数据库已被复制),然后删除重命名的数据库。
您可能会发现这很有用
这是一个未经测试(但已成功编译)的示例。
示例使用了下面的@Entity注解class TheTable
@Entity
data class TheTable(
@PrimaryKey
val id: Long? = null,
val config1: String,
val user1: String,
val user2: String
)
注解的@Database class,TheDatabase,检查重命名的数据库是否存在,如果存在则从重命名的数据库中提取数据并更新基于 id 列的各个行(假设通常是整数 id 列)。 :-
const val DBNAME = "TheDatabase.db"
const val RENAMEDDBNAME = "renamed_$DBNAME"
@Database(entities = [TheTable::class], version = 1, exportSchema = false)
abstract class TheDatabase: RoomDatabase() {
abstract fun getAllDao(): AllDAO
companion object {
var instance: TheDatabase? = null
fun getInstance(context: Context): TheDatabase {
if (instance == null) {
instance = Room.databaseBuilder(context,TheDatabase::class.java, DBNAME)
.allowMainThreadQueries()
.createFromAsset(DBNAME, ppdbc)
.build()
}
return instance as TheDatabase
}
val ppdbc = object : PrepackagedDatabaseCallback() {
@SuppressLint("Range")
override fun onOpenPrepackagedDatabase(db: SupportSQLiteDatabase) {
super.onOpenPrepackagedDatabase(db)
val db_directory = File(db.path).parentFile.path
val renamed_db_path = db_directory + File.separator + RENAMEDDBNAME
val renamed_db_exists = File(renamed_db_path).exists()
if(renamed_db_exists) {
val renamed_db = SQLiteDatabase.openDatabase(renamed_db_path,null,SQLiteDatabase.OPEN_READWRITE)
db.beginTransaction()
val csr = renamed_db.query("thetable",null,null,null,null,null,"id")
val cv = ContentValues()
while (csr.moveToNext()) {
cv.clear()
cv.put("user1",csr.getString(csr.getColumnIndex("user1")))
cv.put("user2",csr.getString(csr.getColumnIndex("user2")))
db.update("thetable",OnConflictStrategy.IGNORE,cv,"id=?", arrayOf(csr.getLong(csr.getColumnIndex("id"))))
}
db.setTransactionSuccessful() //<<<<< only set if all is ok, if not set then changes would be rolled back
db.endTransaction()
csr.close()
renamed_db.close()
File(renamed_db_path).delete()
}
}
}
}
}
- 显然这不是您想要的确切代码,而只是一个可以满足所问问题但很可能需要相应调整的示例。
我的数据库有游戏配置数据和动态用户数据。每次我更新应用程序时,我都会更改很多配置数据(更新记录并添加新记录),并且很容易删除数据库并从资产中重新创建。除了需要保留且不受游戏配置更新影响的用户数据外,所有内容均已更新。
那么我想做些什么来避免冗长的迁移代码:
- 从 Db 中读取用户数据(2 列)将它们临时存储在某个地方
- 删除整个数据库并从我更新的资产中重新创建
- 将用户数据写回这两列。
从技术的角度来看,我总是觉得很难做到这一点,我想知道是否有人知道这是否可能或以前做过?
如果只有两列,我建议您将其从数据库中删除并存储在其他地方。例如在 SharedPreferences 中。或者这不是你能做的?
我遗漏了您的实施细节。
如果您正在使用 Room,这将是试用 Destructive migrations 的绝佳机会。构建数据库时,只需启用破坏性迁移,更新数据库版本时会自动完成工作。
Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
.createFromAsset("database/myapp.db")
.fallbackToDestructiveMigration()
.build()
and it easy to just delete the database and recreate from asset.
而不是删除数据库重命名它,因此它仍然可用。
使用prePackedDatabase
回调应用重命名版本的数据(调用回调时预打包的数据库已被复制),然后删除重命名的数据库。
您可能会发现这很有用
这是一个未经测试(但已成功编译)的示例。
示例使用了下面的@Entity注解class TheTable
@Entity
data class TheTable(
@PrimaryKey
val id: Long? = null,
val config1: String,
val user1: String,
val user2: String
)
注解的@Database class,TheDatabase,检查重命名的数据库是否存在,如果存在则从重命名的数据库中提取数据并更新基于 id 列的各个行(假设通常是整数 id 列)。 :-
const val DBNAME = "TheDatabase.db"
const val RENAMEDDBNAME = "renamed_$DBNAME"
@Database(entities = [TheTable::class], version = 1, exportSchema = false)
abstract class TheDatabase: RoomDatabase() {
abstract fun getAllDao(): AllDAO
companion object {
var instance: TheDatabase? = null
fun getInstance(context: Context): TheDatabase {
if (instance == null) {
instance = Room.databaseBuilder(context,TheDatabase::class.java, DBNAME)
.allowMainThreadQueries()
.createFromAsset(DBNAME, ppdbc)
.build()
}
return instance as TheDatabase
}
val ppdbc = object : PrepackagedDatabaseCallback() {
@SuppressLint("Range")
override fun onOpenPrepackagedDatabase(db: SupportSQLiteDatabase) {
super.onOpenPrepackagedDatabase(db)
val db_directory = File(db.path).parentFile.path
val renamed_db_path = db_directory + File.separator + RENAMEDDBNAME
val renamed_db_exists = File(renamed_db_path).exists()
if(renamed_db_exists) {
val renamed_db = SQLiteDatabase.openDatabase(renamed_db_path,null,SQLiteDatabase.OPEN_READWRITE)
db.beginTransaction()
val csr = renamed_db.query("thetable",null,null,null,null,null,"id")
val cv = ContentValues()
while (csr.moveToNext()) {
cv.clear()
cv.put("user1",csr.getString(csr.getColumnIndex("user1")))
cv.put("user2",csr.getString(csr.getColumnIndex("user2")))
db.update("thetable",OnConflictStrategy.IGNORE,cv,"id=?", arrayOf(csr.getLong(csr.getColumnIndex("id"))))
}
db.setTransactionSuccessful() //<<<<< only set if all is ok, if not set then changes would be rolled back
db.endTransaction()
csr.close()
renamed_db.close()
File(renamed_db_path).delete()
}
}
}
}
}
- 显然这不是您想要的确切代码,而只是一个可以满足所问问题但很可能需要相应调整的示例。