我在我的Android应用程序中使用Room数据库,它提供了一个包含Car个元素的表格,如下所示:

@Entity(tableName = "Cars")
data class Car(
    @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "Id") val id: Int? = null,
    @ColumnInfo(name = "Manufacture") var manufacture: String? = null, 
    @ColumnInfo(name = "Model") var model: String? = null,
    @ColumnInfo(name = "Date_created") val dateCreated: String? = null,
    @ColumnInfo(name = "Date_modified") var dateModified: String? = null,
    @ColumnInfo(name = "Color") var color: String? = null,
    @ColumnInfo(name = "Tags") var tags: String? = null
)

现在,我需要对该表执行一些更改,并按如下方式升级它: 现在,我需要在表中应用一些更改:

@Entity(tableName = "Cars")
data class Car(
    @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "Id") var id: Long = 0,
    @ColumnInfo(name = "Manufacture") var manufacture: String = "", 
    @ColumnInfo(name = "Model") var model: String = "",
    @ColumnInfo(name = "Labels") var labels: List<String> = listOf(),
    @ColumnInfo(name = "Color") var color: String? = null,
    @ColumnInfo(name = "Timestamp") var timestamp: Long = 0L,
    @ColumnInfo(name = "Type") var type: Type = Type.Sedan,
    @ColumnInfo(name = "Folder") var folder: Folder = Folder.Used
)

此次升级将对现有表格进行以下更改:

  • Id栏:val id: Int? = null改为val id: Long = 0.
  • Manufacture栏:var manufacture: String? = null改为var manufacture: String = "".
  • Date_created栏:var dateCreated: String? = null栏已删除.
  • Date_modified重命名为Timestamp,其数据类型也从可为空的String变为不可为空的Long.是否可以在迁移过程中将字符串转换为Long?这些字符串值是转换为字符串timestampe.toString()的时间戳.
  • 数据类型var tags: String? = null的列Tags(由逗号分隔的一个字符串中的标签:例如"Gas,Diesel,Oil")改变为列表var labels: List<String> = listOf()的标签.是否可以在迁移时将标签串转换为列表?
  • TypeFolder是数据类型enum的新列.

我对一些数据类型提出了问题,这些数据类型必须更改为另一种数据类型,因为我找不到任何有关此主题的信息.尽管如此,我还是基于Migrate your Room databaseUnderstanding migrations with Room做了一个小的变通方法,但不确定语法和数据类型转换:

@Database(entities = [Car::class], version = 2, exportSchema = false)
abstract class CarsDatabase : RoomDatabase() {

    abstract val carDao: CarDao

    companion object {

        @Volatile
        private var INSTANCE: CarsDatabase? = null

        private val migration = object : Migration(1, 2) {
            override fun migrate(database: SupportSQLiteDatabase) {
                database.apply {
                    execSQL(
                        "CREATE TABLE cars_tmp (" +
                                "Id INTEGER, " +
                                "Manufacture TEXT, " +
                                "Model TEXT, " +
                                "Timestamp LONG, " + // Not sure about syntax for Long
                                "Labels LIST," + // Not sure about syntax for List<String>
                                "PRIMARY KEY(Id)" +
                                ")"
                    )
                    execSQL("INSERT INTO cars_tmp (" +
                            "Id, Manufacture, Model, Timestamp, Labels, Color) SELECT " +
                            "Id, Manufacture, Model, Date_modified, Tags, Color" + // String of "Date_modified" being converted to Long of "Timestamp" and String of "Tags" being converted to List<String>
                            "FROM Cars"
                    )
                    execSQL("DROP TABLE Car")
                    execSQL("ALTER TABLE cars_tmp RENAME TO Car")
                    execSQL("ALTER TABLE Car ADD COLUMN Type TEXT")
                    execSQL("ALTER TABLE Car ADD COLUMN Folder TEXT")
                }
            }
        }
        
        fun getInstance(context: Context): CarsDatabase = INSTANCE ?: synchronized(this) {
            val instance = Room.databaseBuilder(
                context.applicationContext,
                CarsDatabase::class.java,
                "Cars.db"
            )
                .addMigrations(migration)
                .build()
            INSTANCE = instance
            return instance
        }
    }
}

我不指望有人会做我的工作,但我至少我真的需要一些关于我的问题的答案,以便很好地完成migrations.我对SQL语法非常陌生,因此,我的迁移代码是错误的.

如有任何帮助,敬请惠顾.

编辑: 答案可以在这里找到:How to correctly get the path of Room database on os versions >= 26 sdk?

推荐答案

假设您正在进行更改并需要保留数据(如果您不需要保留任何数据,则只需进行更改并卸载应用程序).

无需计算/猜测Room期望的最简单方法是使用Room生成的SQL.

  1. 首先对表(S)进行更改,即@Entity个注释类.
  2. 已成功编译该项目.
  3. In Android View you will see in the project explorer of Android Studio and there will be a java(generated) expand this until you find a class that is the same name as the @Database annotated class but suffixed with _Impl.
    1. 在你的情况下是CarsDatabase_Impl
  4. 在类中将有一个名为createAllTables的方法(Java中的函数).每个组件(表、索引等)都会有一个_db.execSQL("....");.
    1. 忽略ROOM_MASTER_TABLE,Room将管理此表
  5. 使用SQL(由....表示)作为迁移的基础.这正是Room将使用的SQL,因此符合预期的(实际需要的)模式.
    1. 即准确的数据类型,重要的是精确的约束由您自己决定.

Kotlin相关问答推荐

如何在Kotlin中为两个数据类创建可重用的方法?

Kotlin stlib中是否有用于将列表<;对<;A,B&>;转换为对<;列表<;A&>,列表<;B&>;的函数

如何在Docker中使用Selenium和chromedriver?

Jetpack Compose:当状态从另一个活动改变时强制重组

如何从kotlin中的ArrayList中删除所有元素

如何使用 Android CameraX 自动对焦

Kotlin 中多个 init 块的用例?

kotlin,如何从函数返回类类型

在构造函数中仅注入某些参数

Kotlin DataBinding 将静态函数传递到布局 xml

验证和 DDD - kotlin 数据类

Kotlin如何分派invoke操作符?

Jetpack Compose State:修改类属性

Kotlin数据类打包

如何启用spring security kotlin DSL?

如何根据ArrayList的对象属性值从中获取最小/最大值?

Kotlin,什么时候按map授权?

Kotlin - 错误:Could not find or load main class _DefaultPackage

比较Kotlin的NaN

目前不支持 Gradle 项目的自动库版本更新.请手动更新您的 build.gradle