Room 2.3.0 忽略@NonNull 注释

Room 2.3.0 ignores @NonNull annotations

将 Android Room 从 2.2.6 更新到 2.3.0 后,它停止处理 @androidx.annotation.NonNull 注释,它们现在被忽略了。

升级后安装应用程序时,我得到:

Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number.

用于 2.2.6 房间的示例代码:

@NonNull
@ColumnInfo(name = "span_type")
var spanType: SpanType? = null

升级到 2.3.0 后生成架构更改(我同时设置了 exportSchemaroom.schemaLocation):

在上面的示例中 SpanTypeenum,但我在其他字段上也有这些注释,例如@NonNull var objectId: Long? = null 升级后它们也会被忽略。

我知道我可以使用变通方法,但这不是我想做的 - 我搜索了文档、发行说明等,但找不到任何表明 @NonNull 支持已弃用或删除的信息。我还测试了以前的 kotlin 版本(1.4.x 而不是 1.5.20)并且没有区别。

最接近的 SO 问题可能是 ,但接受的答案显然是错误的 - kotlin 支持 @NonNull 注释。

你知道我遗漏了什么吗?

好的,所以根据评论(感谢@Tenfour04 和@Ken Van Hoeylandt),没有办法再使用 @NonNull

为什么这对我来说是个问题

Room 似乎刚刚禁用了企业应用程序中的安全层之一——数据库约束。由于空检查约束,我的应用程序崩溃了几次,这是正确的(多个线程在同一对象上运行的并发问题)。现在,当 @NonNull 不起作用时,数据库不会崩溃,而是会充满像 id=-1 这样的垃圾数据,因为无法为保存原始类型的字段设置初始空值,例如@ColumnInfo(name = "object_id") lateinit var objectId: Long.

我的解决方法

  • 对于非原始类型,解决方法很简单——我只是替换了这个:
@NonNull
@ColumnInfo(name = "span_type")
var spanType: SpanType? = null

有了这个:

@ColumnInfo(name = "span_type")
lateinit var spanType: SpanType

Room 生成的架构不会改变,所有像 if (spanType == null) {...} 这样的空检查都可以替换为 if (!::spanType.isInitialized) {...}

  • 对于原始类型,解决方法稍微复杂一些

给定字段:

@NonNull
@ColumnInfo(name = "object_id")
var objectId: Long? = null

无法转换为:

@ColumnInfo(name = "object_id")
lateinit var objectId: Long

第一个选项是用 BigInteger 替换 Long:

@ColumnInfo(name = "object_id")
lateinit var objectId: BigInteger

并写一个 @TypeConverter 将 BigInteger 转换为 Long。这可行,但 BigInteger 的内存大小很大 (see this SO) 并且不适合 Android.

我没有使用 BigIntegers,而是为原始类型编写了更简单、轻量级的包装器:

class LateinitPrimitive<T>(t: T?) where T : Number {
    var value: T? = t
}

所以现在我可以声明:

@ColumnInfo(name = "object_id")
lateinit var objectId: LateinitPrimitive<Long>

添加必要的转换器:

@TypeConverter
fun lateinitPrimitiveToLong(lateinitPrimitive: LateinitPrimitive<Long>): Long? = lateinitPrimitive.value

@TypeConverter
fun lateinitPrimitiveFromLong(value: Long?): LateinitPrimitive<Long>? = value?.let { LateinitPrimitive(value) }

我实现了与 Room 2.3.0 之前相同的功能 - 如果 objectId 丢失,我就会变老 android.database.sqlite.SQLiteConstraintException: NOT NULL constraint failed