通过从资产中重新创建来处理房间数据库更新,除了我需要保留的几列之外 - Android 开发

Handling Room Database update by reacreating from asset except for few columns I need to persist - Android Development

我的数据库有游戏配置数据和动态用户数据。每次我更新应用程序时,我都会更改很多配置数据(更新记录并添加新记录),并且很容易删除数据库并从资产中重新创建。除了需要保留且不受游戏配置更新影响的用户数据外,所有内容均已更新。

那么我想做些什么来避免冗长的迁移代码:

  1. 从 Db 中读取用户数据(2 列)将它们临时存储在某个地方
  2. 删除整个数据库并从我更新的资产中重新创建
  3. 将用户数据写回这两列。

从技术的角度来看,我总是觉得很难做到这一点,我想知道是否有人知道这是否可能或以前做过?

如果只有两列,我建议您将其从数据库中删除并存储在其他地方。例如在 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()
                }
            }
        }
    }
}
  • 显然这不是您想要的确切代码,而只是一个可以满足所问问题但很可能需要相应调整的示例。