短版
在将Android版本从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")
}
}
}
}
现在,我想放弃all手动迁移,转而使用自动迁移。我更改了代码,得到了一个错误:
// 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()
我的代码有什么问题,我如何修复这个错误?
更新
我修改了height
字段的注释
从…
@Column(name = "height", defaultValue = "0")
val height: Long = 0L
至
@ColumnInfo(name = "height", defaultValue = "0")
val height: Long = 0L
并按照上述步骤执行,但获得运行时异常。
@ColumnInfo(name = "height")
和数据库版本更新到版本2。然后运行新版本应用程序;@ColumnInfo(name = "height", defaultValue = "0")
(如果我不指定默认值,空间编译器将告诉我必须通过在编译期间抛出异常来指定默认值),并将版本从2提高到3,并将autoMigrations指定为autoMigrations。@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.
发布于 2022-05-30 10:00:39
我已经找到了这个问题的根源。
短版
当房间编译器通过将1.json(模式json文件)与2.json进行比较而生成migration_1_2
时,而不是在生成migration_5_6
或其他迁移时,就会引发此异常。
抛出此异常的原因是: 1.json没有that列,而2.json有nonNull while列,但是没有来自@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)
)
}
}
}
}
}
这段代码告诉我,如果新列是nonNull ,但是在执行从旧数据库到新数据库的时,它没有被指定为默认值,那么Room编译器将抛出一个异常。
看看我的需求和代码:
我想用auto-migration.房间取代ALL手动迁移
我的密码是:
// column declaration
// a non null type long
// and has not specified default value on @ColumnInfo
@ColumnInfo(name = "height")
val height: Long = 0L
当Room Compiler处理@ColumnInfo在height
字段上注释以生成migration_1_to_2
时,它会发现height
字段it nonNull (by XType.nullability
)并且没有指定默认值(通过读取@ColumnInfo.defalutValue
)。
然后,通过对1.json和2.json进行比较,发现height
是新的列。
有一个矛盾:height
列是nonNull,但它没有默认值!为了让开发人员知道这个问题,空间编译器选择抛出异常。
通过分析源代码,我们知道当空间编译器生成migration_1_to_2
时抛出异常,而不是在生成migration_5_to_6
或其他迁移时抛出异常。因此,即使我们为height
指定了默认值,房间编译器仍然会抛出这个问题--当前版本是6,房间编译器将生成6.json,而不是2.json,默认规范不会写到2.json。
我认为修改历史模式json文件是危险的,所以我放弃了用自动迁移代替手动迁移的想法。
发布于 2022-05-24 09:39:24
,即使我通过@ColumnInfo指定了默认值,但仍然有相同的错误:
我相信(根据较长的版本)您的问题是使用的是@Column(....
而不是@ColumnInfo(....
。
所以改变
@Column(name = "height", defaultValue = "0")
val height: Long = 0L
至
@ColumnInfo(name = "height", defaultValue = "0")
val height: Long = 0L
使用第1版和第2版(使用更正的@ColumnInfo)进行测试,使用两次运行-一次在版本1,然后使用2次运行,使用:-
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}")
}
}
}
版本
通过应用程序检查的数据库是:-
这两种记录都是:-
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添加一个基本注释),然后:-
https://stackoverflow.com/questions/72359140
复制相似问题