当前位置: 首页 > 知识库问答 >
问题:

房间自动迁移的混淆,没有空列,但没有诽谤值

段干弘毅
2023-03-14

在将Android-Room版本从2.2.x更新到2.4.x后,我决定使用自动迁移功能来帮助我编写更少的代码。因此,我想弃用所有手动编写的迁移,而改用自动迁移。但是我在使用自动迁移时遇到错误:

// Compile Time Error: 
// New NOT NULL column'height' added with no default value specified. 
// Please specify the default value using @ColumnInfo.

@ColumnInfo(name = "height")
val height: Long = 0L

即使我以这种方式指定了默认值,但仍然得到相同的错误:

@ColumnInfo(name = "height", defaultValue = "0")
val height: Long = 0L

我的代码有什么问题,如何修复此错误?

我有两个版本数据库:

版本 1

@Entity(tableName = "user")
data class User(
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    val id: Long = 0L,
    @ColumnInfo(name = "name")
    val name: String = ""
)

@Database(
    entities = [User::class],
    version = 1
)
abstract class UserDB : RoomDatabase()

然后在版本2中,我在用户表中添加了一个名称为“高度”的列:

版本2

@Entity(tableName = "user")
data class User(
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    val id: Long = 0L,
    @ColumnInfo(name = "name")
    val name: String = "",
    @Column(name = "height")
    val height: Long = 0L
)

@Database(
    entities = [User::class],
    version = 2
)
abstract class UserDB : RoomDatabase() {
    object ManualMigrations {
        val M_1_2 = object : Migration(1, 2) {
            override fun migrate(database: SupportSQLiteDatabase) {
                database.execSQL("ALTER TABLE user ADD COLUMN height INTEGER NOT NULL DEFAULT 0")
            }
        }
    }
}

现在我想弃用所有手动迁移并改用自动迁移。我更改了代码并收到错误:

// Compile Time Error: 
// New NOT NULL column'height' added with no default value specified. 
// Please specify the default value using @ColumnInfo.
@Database(
    entities = [User::class],
    autoMigrations = [
        AutoMigration(from = 1, to = 2)
    ],
    version = 2
)
abstract class UserDB : RoomDatabase()

即使我已经通过@ColumnInfo指定了默认值,但仍然得到相同错误:

// Compile Time Error: 
// New NOT NULL column'height' added with no default value specified. 
// Please specify the default value using @ColumnInfo.
@Entity(tableName = "user")
data class User(
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    val id: Long = 0L,
    @ColumnInfo(name = "name")
    val name: String = "",
    // specify default value by this way
    @Column(name = "height", defaultValue = "0")
    val height: Long = 0L
)

@Database(
    entities = [User::class],
    autoMigrations = [
        AutoMigration(from = 1, to = 3)
    ],
    // Since I have modify the structure of table, 
    // I increase the version.
    version = 3
)
abstract class UserDB : RoomDatabase()

我的代码有什么问题,如何修复此错误?

我已经修改了<code>高度

@Column(name = "height", defaultValue = "0")
val height: Long = 0L

@ColumnInfo(name = "height", defaultValue = "0")
val height: Long = 0L

并执行上述步骤,但得到运行时异常

  1. 完全卸载应用程序,并安装数据库版本= 1的应用程序;
  2. 添加带有注释的新列高:@ColumnInfo(name = “height”)并通过编写手动迁移将数据库版本更新到版本2。然后运行新版本app;
  3. 要使用自动迁移,我删除了手动迁移并更新高度字段注释:@ColumnInfo(name = “height”,defaultValue = “0”)(如果我不指定默认值,房间编译器会告诉我,我必须通过在编译时抛出 Exception来指定默认值),并将版本从2增加到3,并指定autoMigrations到@Database
@Database(
    entities = [Worker::class],
    autoMigrations = [AutoMigration(from = 1, to = 3)],
    version = 3
)
abstract class UserDB : RoomDatabase()

然后在运行时执行这些代码并获得运行时异常:

val userDB = buildUserDB().openHelper.readableDatabase
            Log.d(TAG, "columnNames=${workerDB.query("SELECT * FROM user").columnNames.toList()}")

// java.lang.IllegalStateException: A migration from 2 to 3 was required but not found. 
// Please provide the necessary Migration path via RoomDatabase.Builder.addMigration(Migration ...) 
// or allow for destructive migrations via one of the 
// RoomDatabase.Builder.fallbackToDestructiveMigration* methods.

共有2个答案

商飞龙
2023-03-14

即使我已经通过@ColumnInfo指定了默认值,但仍然得到相同错误:

我相信(根据长版本)您的问题是您使用了< code>@Column(....而不是< code>@ColumnInfo(....。

所以改变

@Column(name = "height", defaultValue = "0")
val height: Long = 0L

@ColumnInfo(name = "height", defaultValue = "0")
val height: Long = 0L

使用两个运行来测试上述内容,一个在版本1,然后运行2(使用更正的@ColumnInfo),使用:-

class MainActivity : AppCompatActivity() {
    lateinit var db: UserDB
    lateinit var dao: UserDao
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        db = UserDB.getInstance(this)
        dao = db.getUserDao()

        dao.insert(User(name = "NAME001Version$DATABASE_VERSION"))
        dao.insert(User(name = "NAME002Version$DATABASE_VERSION"))
        dao.insert(User(name = "NAME003Version$DATABASE_VERSION"))

        for (u in dao.getAllUsers()) {
            Log.d("DBINFO","User ID = ${u.id} UserName is ${u.name} Version is ${DATABASE_VERSION}")
        }
    }
}
    < li >其中DATABASE_VERSION是版本号

通过应用程序检查的数据库为:-

两次运行的日志:-

2022-05-24 19:32:57.784 D/DBINFO: User ID = 1 UserName is NAME001Version1 Version is 1
2022-05-24 19:32:57.784 D/DBINFO: User ID = 2 UserName is NAME002Version1 Version is 1
2022-05-24 19:32:57.784 D/DBINFO: User ID = 3 UserName is NAME003Version1 Version is 1



2022-05-24 19:38:41.963 D/DBINFO: User ID = 1 UserName is NAME001Version1 Version is 2
2022-05-24 19:38:41.963 D/DBINFO: User ID = 2 UserName is NAME002Version1 Version is 2
2022-05-24 19:38:41.964 D/DBINFO: User ID = 3 UserName is NAME003Version1 Version is 2
2022-05-24 19:38:41.964 D/DBINFO: User ID = 4 UserName is NAME001Version2 Version is 2
2022-05-24 19:38:41.964 D/DBINFO: User ID = 5 UserName is NAME002Version2 Version is 2
2022-05-24 19:38:41.964 D/DBINFO: User ID = 6 UserName is NAME003Version2 Version is 2

要复制此问题(必须为@Column添加基本注释),然后:-

方增
2023-03-14

我已经找到了这个问题的根本原因。

当房间编译器通过将1.json(模式json文件)与2.json比较生成migration_1_2时,不会在生成migration_5_6或其他迁移时引发此异常。

引发此异常的原因是1.json没有高度列,而2.json有非空高度列,但没有@ColumnInfo中的默认值。

因为修改2.json(历史模式json文件)是危险的,所以我放弃了用自动迁移代替手动迁移的想法。

让我在Room Compiler展示一段代码来解释为什么会出现这个问题:

// SchemaDiffer.kt

class SchemaDiffer(...) {

    ...

    private fun processAddedTableAndColumns(...) {
        toColumns.values.forEach { toColumn ->
            val match = fromColumns[toColumn.columnName]
            if (match == null) {
                if (toColumn.isNonNull && toColumn.defaultValue == null) {
                    // >>>>>>> ** HERE! ** >>>>>>>
                    // New NOT NULL column'height' added with no default value specified.
                    diffError(
                        newNotNullColumnMustHaveDefaultValue(toColumn.columnName)
                    )
                }
            }
        }
    }
}

这段代码告诉我,如果新列为非空,但在从旧数据库自动迁移到新数据库时没有指定默认值,那么Room编译器将抛出异常。

看看我的需求和我的代码:

    < li >我想将所有手动迁移替换为房间自动迁移。

和我的代码:

// column declaration
// a non null type long
// and has not specified default value on @ColumnInfo
@ColumnInfo(name = "height")
val height: Long = 0L

当房间编译器进程@ColumnInfo高度字段上批注以生成migration_1_to_2时,它会发现高度字段非空值(通过 XType.nullability)并且没有指定默认值(通过读取 @ColumnInfo.defalutValue)。

然后,通过将 1.json 与 2.json 进行比较,房间编译器会发现高度为 NEW 列。

有一个矛盾:高度列是非空的,但它没有默认值!为了让开发人员知道这个问题,房间编译器选择抛出异常。

通过分析源代码,我们知道当房间编译器生成migration_1_to_2时会引发异常,而不是在生成migration_5_to_6或其他迁移时。所以即使我们指定了高度的默认值,房间编译器仍然会抛出这个问题——当前版本是6,房间编译器会生成6.json,而不是2.json,默认规范不是写到2.json。

我认为修改历史架构json文件是危险的,所以我放弃了用自动迁移取代手动迁移的想法。

 类似资料:
  • 问题内容: 我想在下面的HTML片段中选择BONKERS。它的区别在于,它是单独存在的,而其所有兄弟姐妹都包含。是显而易见的选择,但由于文本节点而无法使用。我以为我知道这些东西,但这正驱使我疯狂。 我需要一个纯CSS解决方案(不能选择JS),并且无法控制源HTML。 ! 问题答案: 您可以按照这种方法。通过所需的CSS 设置元素的样式,然后重置可在样式中继承的CSS样式,即: CSS: 您可能不需

  • 问题内容: 我尝试执行的几乎所有操作都收到“设备上没有剩余空间”错误。即使使用制表符自动完成命令! 但是当我做df -hi时得到: 在我看来,那里有无数的空间。df -i看起来也类似: 我在Amazon EC2 ubuntu 12.04实例上。 以下是错误弹出的一些示例: 但是服务器似乎正在运行,并且一切似乎都正常。到底是怎么回事? 问题答案: 可能是内存不足或其他地方有空间,它提示系统安装了溢出

  • 问题内容: 我到目前为止发现的 所有@entity批注的类在编译期间进行处理,并且生成了Database的实现类。然后,在访问数据库之前,将调用此生成的类的validateMigration方法。这个validateMigration方法通过原始查询与现有的数据库架构进行验证 (请参见android.arch.persistence.room.util.TableInfo.java的L208) 现

  • 我正在使用2.4.0-ALPHA01房间

  • Django v1.11.5 我试图安装GeoDjango来玩谷歌地图。 我为MAC安装了PostgreSQL应用程序,并安装了。我还使用自制软件安装GDAL。 要添加的已编辑设置.py: 但是,当我运行migrate时,我得到: 我创建用户使用: 完全错误: 运行迁移:Applying users.0011_location…Traceback(最后一次调用):在execute_from_com

  • 我正在编写一个minecraft mod,当我在forge 1.8.9中运行它时,我得到了。然而,当我运行另一个mod时,这个mod可以工作。 我的mod的反编译类文件如下所示 而另一个看起来像 无论我如何构建我的mod,我的类文件永远不会像这样结束。如何让gradle混淆代码?