【问题标题】:Room database: NOT NULL constraint failed at deletion房间数据库:NOT NULL 约束在删除时失败
【发布时间】:2019-09-24 09:28:31
【问题描述】:

我有以下实体,SessionLabel

@Entity(
    foreignKeys =
    @ForeignKey(
            entity = Label.class,
            parentColumns = {"id", "archived"},
            childColumns = {"labelId", "archived"},
            onUpdate = CASCADE,
            onDelete = SET_DEFAULT))
    public class Session {

    @PrimaryKey(autoGenerate = true)
    public long id;

    @Nullable
    public String label = null;

    public boolean archived = false;
}

@Entity(primaryKeys = {"id", "archived"})
public class Label {

    @NonNull
    public String id;

    public boolean archived = false;
}

删除附加到SessionLabel 时,我得到NOT NULL constraint failedSession.archived

我在这里做错了什么?

【问题讨论】:

  • childColumns 同名 "archived"parentColumns 在这种情况下可以吗?这是否导致错误?

标签: android sqlite android-room


【解决方案1】:

我认为问题在于使用 public boolean archived = false; 中的 boolean 类型。

由于 boolean 是 Java 主要类型,因此它具有隐式 @NonNull(表是使用 NOT NULL 约束创建的)

使用 Room,您无法(我相信)设置 SQLite 表中生成的默认值,因此在使用 onDelete SET_DEFAULT 时,默认值将是 NULL,因为没有设置实际的默认值。

  • Note Room 现在支持在@ColumnInfo 注解中使用defaultValue() 的默认值。

  • 使用= false 不会影响实际的SQLite 表,即列定义,如果使用Boolean,将是labelId INTEGER 而不是labelId INTEGER DEFAULT 0

假设 NULL 是可接受的,那么您可以使用 Boolean(对象)代替 boolean(主要类型)。对象可以为空。

即在会话中使用

public Boolean archived = false;

视觉解释:-

以下是 Room 为会话实体生成的代码,但添加了一个附加列:-

.........
public boolean archived = false;
public Boolean other_archived = false; //<<<<<<<<<< ADDED
........

房间生成的代码:-

CREATE TABLE IF NOT EXISTS `Session` (
    `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 
    `label` TEXT, 

    `archived` INTEGER NOT NULL, /*<<<<<<<<<< boolean so NOT NULL */ 
    `other_archived` INTEGER, /*<<<<<<<<<< Boolean so no NOT NULL */
    FOREIGN KEY(`label`, `archived`) REFERENCES `Label`(`id`, `archived`) ON UPDATE CASCADE ON DELETE SET DEFAULT)

额外

如果您希望 Session 表中的归档列为 0(假),那么您必须修改该表。这将需要

  1. 创建一个相同的替换表,除了标签列定义为 label INTEGER DEFAULT 0(即添加了 DEFAULT 0)
  2. 将 Session 表中的数据复制到替换表中,例如INSERT INTO replacement_Session SELECT * FROM Session;
  3. 重命名 Session 表。
  4. 将替换表重命名为 Session。
  5. 如果高兴删除重命名的 Session 表。

或者(我认为)您可以使用 AFTER UPDATE TRIGGER 将 NULL 更改为 0(假)。

必须在 Room 之外或在构建 RoomDatabase 之前完成任一选项。

【讨论】:

  • 使用Boolean 代替boolean 有助于解决问题。在 Dao 中,在这种情况下,我必须按 archived = 0 or archived = NULL 进行过滤。
【解决方案2】:

参考this。如果您使用 Kotlin 进行编码,为了使模型的字段可以为空,请不要使用 @Nullable 而是使用“?”使字段可以为空。 以下字段不可为空。

@Entity(tableName = "books")
data class Book(
    @PrimaryKey
    val id: Int,
    val title: String,
    val description: String,
    val info: String,
    val type: Int,
    val url: String
)

但以下字段可以为空:

@Entity(tableName = "books")
data class Book(
    @PrimaryKey
    val id: Int,
    val title: String?,
    val description: String?,
    val info: String?,
    val type: Int,
    val url: String?
)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-04-22
    • 1970-01-01
    • 2020-10-28
    • 1970-01-01
    • 2018-08-24
    • 2022-01-19
    • 2021-11-16
    相关资源
    最近更新 更多