如何在Android中使用删除表操作处理自动迁移

pnwntuvh  于 11个月前  发布在  Android
关注(0)|答案(1)|浏览(132)

我一直在尝试自动迁移,其中一个表将被删除,到我的房间数据库。我指定自动迁移如下:

@Database(
  version = 9,
  entities = [...],
  views = [...],
  exportSchema = true,
  autoMigrations = [
    ...
    AutoMigration(8, 9)
  ]
)
@TypeConverters(
  value = [...],
  builtInTypeConverters = BuiltInTypeConverters(enums = BuiltInTypeConverters.State.ENABLED)
)

字符串
当我尝试构建项目时,我得到以下错误:

AutoMigration Failure: Please declare an interface extending 'AutoMigrationSpec',
and annotate with the @RenameTable or @DeleteTable annotation to specify the change
to be performed:
1) RENAME:
    @RenameTable.Entries(
        @RenameTable(
            fromTableName = "some_table",
            toTableName = <NEW_TABLE_NAME>
        )
    )
2) DELETE:
    @DeleteTable.Entries(
        @DeleteTable(
            tableName = "some_table"
        )
    )


我还从实体列表中删除了表类,即SomeTable::class。
在错误之后,我声明一个AutoMigrationSpec类,用@DeleteTable注解,如下所示:

@DeleteTable("some_table")
class Version9 : AutoMigrationSpec


并添加声明它作为规范,如下所示:

@Database(
  version = 9,
  entities = [...],
  views = [...],
  exportSchema = true,
  autoMigrations = [
    ...
    AutoMigration(8, 9, Version9::class)
  ]
)
@TypeConverters(
  value = [...],
  builtInTypeConverters = BuiltInTypeConverters(enums = BuiltInTypeConverters.State.ENABLED)
)


我得到了以下相同的错误。
如果我使用规范作为接口而不是类,

@DeleteTable("some_table")
interface Version9 : AutoMigrationSpec


我得到一个额外的错误,The AutoMigration spec type must be a class.
在声明AutoMigrationSpec时,我还尝试使用以下格式:

@DeleteTable.Entries(DeleteTable("some_table"))
class Version9 : AutoMigrationSpec


但是,我得到了最初的错误。

mm5n2pyu

mm5n2pyu1#

我看不出你提供的代码有什么问题。
基于您的代码,假设其他代码,编码使之前和之后运行相对简单,使用KAPT而不是KSP并使用2.5.2 Room库,然后代码按照日志输出的预期工作(参见下面的代码):

第1部分为第一版,有2个表(T1和T2):-

2024-01-02 11:29:24.237 D/DBINFO1SCHEMA: SQL FOR COMPONENT is 
        CREATE TABLE android_metadata (locale TEXT)
2024-01-02 11:29:24.237 D/DBINFO1SCHEMA: SQL FOR COMPONENT is 
        CREATE TABLE `T1` (`t1Id` INTEGER, `t1Name` TEXT NOT NULL, PRIMARY KEY(`t1Id`))
2024-01-02 11:29:24.237 D/DBINFO1SCHEMA: SQL FOR COMPONENT is 
        CREATE TABLE `T2` (`t2Id` INTEGER, `t2Name` TEXT NOT NULL, PRIMARY KEY(`t2Id`))
2024-01-02 11:29:24.237 D/DBINFO1SCHEMA: SQL FOR COMPONENT is 
        CREATE TABLE room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)
2024-01-02 11:29:24.308 D/DBINFO1: logDBInfo function INVOKED and STARTED for DATABASE VERSION 1
2024-01-02 11:29:24.308 D/DBINFO1T1: T1 ID is 1 NAME is T1Test001_1
2024-01-02 11:29:24.308 D/DBINFO1T1: T1 ID is 2 NAME is T1Test002_1
2024-01-02 11:29:24.308 D/DBINFO1T1: T1 ID is 3 NAME is T1Test003_1
2024-01-02 11:29:24.308 D/DBINFO1T2: T2 ID is 1 NAME is T2Test001
2024-01-02 11:29:24.309 D/DBINFO1: logDBInfo function INVOKED and STARTED for DATABASE VERSION 1

字符串

第二个版本(迁移)的第2部分,只有一个表(T1):-

2024-01-02 11:33:47.231 D/DBINFO2SCHEMA: SQL FOR COMPONENT is 
        CREATE TABLE android_metadata (locale TEXT)
2024-01-02 11:33:47.231 D/DBINFO2SCHEMA: SQL FOR COMPONENT is 
        CREATE TABLE `T1` (`t1Id` INTEGER, `t1Name` TEXT NOT NULL, PRIMARY KEY(`t1Id`))
2024-01-02 11:33:47.231 D/DBINFO2SCHEMA: SQL FOR COMPONENT is 
        CREATE TABLE room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)
2024-01-02 11:33:47.237 D/DBINFO2: logDBInfo function INVOKED and STARTED for DATABASE VERSION 2
2024-01-02 11:33:47.237 D/DBINFO2T1: T1 ID is 1 NAME is T1Test001_1
2024-01-02 11:33:47.237 D/DBINFO2T1: T1 ID is 2 NAME is T1Test002_1
2024-01-02 11:33:47.237 D/DBINFO2T1: T1 ID is 3 NAME is T1Test003_1
2024-01-02 11:33:47.238 D/DBINFO2: logDBInfo function INVOKED and STARTED for DATABASE VERSION 2

  • 即,模式已按预期改变(T2不再存在),沿着还有剩余的数据(仅在DB版本为1时写入)。

完整的代码是:

所有DB代码(根据第2次运行 (参见第1次运行的注解)):-

const val DBVERSION=2;
const val DBFILENAME = "the_dataabse.db"

@Entity
data class T1(
    @PrimaryKey
    var t1Id: Long?=null,
    var t1Name: String
)
@Entity
data class T2(
    @PrimaryKey
    var t2Id: Long?=null,
    var t2Name: String
)

@Dao
interface T1DAOs {
    @Insert
    fun insert(t1: T1): Long
    @Query("SELECT * FROM t1")
    fun getAllT1s(): List<T1>
}
@Dao
interface T2DAOs {
    @Insert
    fun insert(t2: T2): Long
    @Query("SELECT * FROM t2")
    fun getAllT2s(): List<T2>
}
@Database(entities = [T1::class/*,T2::class COMMENTED OUT FOR V2 */], version = DBVERSION, exportSchema = true
    ,autoMigrations = [AutoMigration(1,2,Version1To2::class)]
)
abstract class TheDatabase: RoomDatabase() {
    abstract fun getT1DAOs(): T1DAOs
    /*abstract fun getT2DAOs(): T2DAOs COMMENTED OUT FOR V2*/

    companion object {
        private var instance: TheDatabase?=null
        fun getInstance(context: Context): TheDatabase {
            if (instance==null) {
                instance = Room.databaseBuilder(context,TheDatabase::class.java, DBFILENAME)
                    .allowMainThreadQueries()
                    .build()
            }
            return instance as TheDatabase
        }
    }
}
@DeleteTable(tableName = "T2")
class Version1To2: AutoMigrationSpec

活动代码 (为简洁起见,在主线程上运行,第二次运行时再次运行,再次参考第一版的注解):-

const val TAG = "DBINFO"
class MainActivity : AppCompatActivity() {
    lateinit var db: TheDatabase
    lateinit var t1DAOs: T1DAOs
    lateinit var t2DAOs: T2DAOs
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        db = TheDatabase.getInstance(this)
        db.openHelper.writableDatabase /* force database open and thus create/migration etc */
        logDBSchema()
        t1DAOs = db.getT1DAOs()
        if (DBVERSION==1) {
            t1DAOs.insert(T1(t1Name ="T1Test001_${DBVERSION}"))
            t1DAOs.insert(T1(t1Name = "T1Test002_${DBVERSION}"))
            t1DAOs.insert(T1(t1Name = "T1Test003_${DBVERSION}"))
            /* COMMENTED OUT FOR V2
            t2DAOs = db.getT2DAOs()
            t2DAOs.insert(T2(t2Name = "T2Test001"))
            logDBInfo(t1DAOs.getAllT1s(),t2DAOs.getAllT2s())

             */
        } else {
            logDBInfo(t1DAOs.getAllT1s())
        }
    }

    fun logDBInfo(rowsFromTable1: List<T1> = listOf(), rowsFromTable2: List<T2> = listOf()) {
        Log.d(TAG+ DBVERSION,"logDBInfo function INVOKED and STARTED for DATABASE VERSION ${DBVERSION}")
        for (t1 in rowsFromTable1) {
            Log.d(TAG+ DBVERSION+"T1","T1 ID is ${t1.t1Id} NAME is ${t1.t1Name}")
        }
        for (t2 in  rowsFromTable2) {
            Log.d(TAG+ DBVERSION+"T2","T2 ID is ${t2.t2Id} NAME is ${t2.t2Name}")
        }
        Log.d(TAG+ DBVERSION,"logDBInfo function INVOKED and STARTED for DATABASE VERSION ${DBVERSION}")
    }
    fun logDBSchema() {
        var cursor: Cursor? = null
        if(db != null && db.isOpen) {
            cursor=db.openHelper.writableDatabase.query("SELECT sql FROM sqlite_master")
        } else {
            cursor =SQLiteDatabase.openDatabase(this.getDatabasePath(
                DBFILENAME).path,null,0).rawQuery("SELECT sql FROM sqlite_master",null)
        }
        if (cursor != null) {
            while (cursor.moveToNext()) {
                Log.d(TAG+ DBVERSION+"SCHEMA","SQL FOR COMPONENT is \n\t${cursor.getString(0)}")
            }
        }
    }
}

结论

基本上,正如最初所说的,代码没有明显的问题。

  • 需要注意的是,使用的是2.5.2房间库,如果这不是您使用的版本,请尝试使用2.5.2
  • 这是不是未知的错误出现,例如,这个Q & A处理库中的一个错误。该链接也有一个修复使用手动迁移。
  • 使用手动迁移可以更好地控制迁移过程
  • 如果使用KSP,然后尝试使用KAPT,则使用KAPT时遇到的问题较少。

相关问题