如何使用Android Room Persistence Library将列注释为NOT NULL

32

我的数据类看起来像这样

@Entity(tableName = "items")
data class Item(
        @ColumnInfo(name = "name") var name: String = "",
        @ColumnInfo(name = "room") var room: String = "",
        @ColumnInfo(name = "quantity") var quantity: String = "",
        @ColumnInfo(name = "description") var description: String = "",
        @PrimaryKey(autoGenerate = true)
        @ColumnInfo(name = "id") var id: Long = 0
)

Room使用SQLite,而SQLite支持数据库中的NOT NULL列。我尝试使用@NonNull注释列,但并没有起作用。

有没有方法使Room数据库中的列不可为空?


5
默认情况下它是“非空”的吗?因为我也在使用 Room 持久性库,有一次不小心向数据库发送了 null,结果引发了异常。 - Sachin Chandil
我得到了一些空字段,比如空字符串,它可以正常工作,所以我猜那不是问题的原因。 - Tuby
那么你的问题是什么? - Sachin Chandil
5个回答

46

对于Java,你应该使用注释 @android.support.annotation.NonNull

不要与@io.reactivex.annotations.NonNull混淆


你刚刚拯救了我的一天! - codeyourstack
如果我们需要在Room中使用RxJava,那么在创建实体时应该导入哪个库? - Ishwor Khanal
1
Ishwor Khanal,同@android.support.annotation.NonNull一样,rx的注释主要用于内部目的。 - Nokuap

32

为了确定 NOT NULL 的确切注解,我查阅了此链接提供的所有注解列表:android.arch.persistence.room,但未能找到任何相关的注解:

enter image description here

我的假设是,由于您正在使用 Kotlin,Kotlin 默认将 var 视为非可选项,您声明的所有属性均为 NOT NULL。要将 var 声明为可选项,则可能必须使用 ? 运算符,以下是示例:

var name: String // NOT NULL 
var name: String? // OPTIONAL

根据评论更新:

我猜你可能对NULL和空字符串感到困惑。NULL表示无值,在你提供的共享代码中,你为每个列提供了一个默认值,即空字符串,并且它不等于NULL,因此即使你没有为该属性提供值,它也会默认分配一个空字符串给它。

尝试删除属性的值分配运算符和右手边的值,然后在不为该属性分配值的情况下插入记录,你应该会得到适当的错误提示。

基本上,将以下语句转换:

@ColumnInfo(name = "name") var name: String = ""

为了

@ColumnInfo(name = "name") var name: String

我的理解是数据库中的 NOT NULL 表示其不能是空字段,需要有一个赋值的值,这样空字符串 "" 将被视为 NULL。在 Kotlin 中,String 表示其具有不为 null 的对象分配,默认情况下为空字符串。我只希望当分配了空字符串时,我的数据库会抛出异常。我猜想在 Room 中做不到这一点。 - Tuby
我会将其标记为正确的,因为它澄清了我对空字符串和null之间的困惑。然而,在我的特定情况下,您提出的转换语句(第二个)无法编译,因为Room需要默认构造函数,并且为此每个参数都必须具有默认值。但是感谢您的帮助! - Tuby
谢谢,最初这个答案对我没有起作用,因为我在变量上设置了private的范围。 - olfek
1
谢谢。使用NonNull注释并设置属性而不进行分配也为我解决了问题@Devarshi - mochadwi
“需要分配一个值,这样空字符串""将被视为NULL”那不正确。空字符串不是null。Null就是null。空字符串是长度为0的字符串对象。” - user1713450

12

对于Java,我在@ColumnInfo之后使用了@NonNull

@ColumnInfo(name = "column_name")
@NonNull private String str;

确保你在类中导入了正确的库:

import android.support.annotation.NonNull;

4
请注意,原始类型(int、long等)始终被视为NonNull,您不必将它们注释为NonNull。 - Beer Me
@BeerMe,你知道这个在哪里有文档吗?我在这篇文章中找到了原始类型的管理提及。似乎浮点类型不被视为相同类型,并且默认情况下是可空的。 - shield
@shield,我手头没有实际的文档,但是这个答案非常好地概括了它:https://dev59.com/3Ggu5IYBdhLWcg3w0qRK#11047335 - Beer Me
此外,在您提供的文章中,我没有看到任何有关浮点类型的参考,但它确实说“在Room中,默认情况下所有原始类型都是非空的”。 - Beer Me
1
@BeerMe,谢谢。关于浮点类型,是我的错误。在我查看的示例中,列类型不是原始类型。它被声明为Float,而不是float,因此默认情况下可为空。 - shield

11

对于使用 Kotlin 的人的补充答案:

请注意,将类型标记为非可选类型将自动使其不为 null (可选类型则不会这样)。

您可以通过在数据库上使用 @Database(exportSchema = true) 来检查 Room 生成的模式。

例如,我有像这样的东西:

@Entity(tableName = "messages")
data class Message (
        @PrimaryKey
        val messageId: UUID = UUID.randomUUID(),
        val date: Date = Date(),
        val receivedDate: Date? = null
)

在生成的模式中,我可以阅读到:

"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (
    `messageId` TEXT NOT NULL, 
    `date` INTEGER NOT NULL, 
    `receivedDate` INTEGER, 
    PRIMARY KEY(`messageId`)
)"

(注:这里的日期类型是Int,UUID是字符串,因为我在其他地方使用了转换器)


5

为了绕过实际表格约束,您可以使用单个TypeConverter类方法来确保您的字符串成员不为空。

例如,对于非空字符串,您可以使用:

public class RoomConverterNullString {
    @TypeConverter
    public static String fromNullToString(String value) {
        if (value == null) {
            return "";
        } else {
            return value;
        }
    }
}

你可以这样简化它,当然:
public class RoomConverterNullString {
    @TypeConverter
    public static String fromNullToString(String value) {
        return (value == null) ? "" : value;
    }
}

您可以像这样使用它:

@TypeConverters(RoomConverterNullString.class)
public String stringMember;

当值为null时,任何赋值、更新/插入操作都将存储为空字符串值,并且任何检索都会强制将null值转换为空字符串。

您可以对其他类型以及任何希望拥有的默认值执行此操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接