在Room数据库中为多个列添加唯一约束条件

65

我房间里有一个实体

@Entity(foreignKeys ={
        @ForeignKey(entity = Label.class, parentColumns = "_id", childColumns = "labelId", onDelete = CASCADE),
        @ForeignKey(entity = Task.class, parentColumns = "_id", childColumns = "taskId", onDelete = CASCADE)
})
public class LabelOfTask extends Data{
    @ColumnInfo(name = "labelId")
    private Integer labelId;
    @ColumnInfo(name = "taskId")
    private Integer taskId;
}

此实体的SQL语法如下所示

CREATE TABLE `LabelOfTask` (
    `_id` INTEGER PRIMARY KEY AUTOINCREMENT,
     `labelId` INTEGER,
     `taskId` INTEGER,
     FOREIGN KEY(`labelId`) REFERENCES `Label`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE ,
     FOREIGN KEY(`taskId`) REFERENCES `Task`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE
 );

如果我想将以下约束条件追加到表的自动生成SQL模式中,我需要在实体类中进行何种更改或注释?

unique (labelId, taskId)

最终,我想使用room库使表格(或实体)中的labelId和taskId的组合唯一。


1
不支持在列上使用普通的UNIQUE约束,除非通过索引。 - lib4backer
1
索引?你是指主键吗? - Marian Paździoch
5个回答

118

除了通过索引之外,对列设置明确的唯一约束不受支持。

您可以通过将@Index注释的unique属性设置为true来强制执行此唯一性属性。以下代码示例(Java)防止表格具有包含相同firstName和lastName列值集的两行:

@Entity(indices = {@Index(value = {"first_name", "last_name"},
        unique = true)})
class User {
    @PrimaryKey
    public int id;

    @ColumnInfo(name = "first_name")
    public String firstName;

    @ColumnInfo(name = "last_name")
    public String lastName;

    @Ignore
    Bitmap picture;
}

下面是注释的 Kotlin 等效形式:

@Entity(indices = [Index(value = ["first_name", "last_name"], unique = true)])

您可以对代码进行以下更改以添加唯一约束条件:

@Entity(foreignKeys ={
        @ForeignKey(entity = Label.class, parentColumns = "_id", childColumns = "labelId", onDelete = CASCADE),
        @ForeignKey(entity = Task.class, parentColumns = "_id", childColumns = "taskId", onDelete = CASCADE)},
        indices = {@Index(value = {"labelId", "taskId"},
                unique = true)}
)
public class LabelOfTask extends Data{
    @ColumnInfo(name = "labelId")
    private Integer labelId;
    @ColumnInfo(name = "taskId")
    private Integer taskId;
}

5
设置单个属性为唯一的? - zulkarnain shah
1
我尝试回答了你的问题,但我认为我把它粘贴到了错误的位置 :) 请在下面检查一下 :D - Ercan
1
你好!如果尝试添加第二个相同的值会发生什么? - Valeriy
1
对于 Kotlin,您可以这样编写:foreignKeys = [ForeignKey( entity = Label::class, parentColumns = ["_id"], childColumns = ["labelId"], onDelete = ForeignKey.CASCADE)], indices = [Index(value = ["labelId", "taskId"], unique = true)] - Jamal S
@Valeriy 如果发生冲突,将基于您定义的冲突策略。 - Irfan Ul Haq
显示剩余2条评论

54

如果您想让单列变为唯一的,只需编写

@Entity(indices = [Index(value = ["name"], unique = true)])

如何创建复合唯一键和复合主键? - K Pradeep Kumar Reddy
@KPradeepKumarReddy,对于那个实体注释,有一个primaryKeys字段。 - Afzal N

13

对于单列唯一性

@Entity(indices = {@Index(value = {"first_name"},unique = true)})

对于多列唯一性

@Entity(indices = {@Index(value = {"first_name", "last_name"},unique = true)}) 

3

由于无法评论,因此我会在此添加。如果要手动迁移您的Room DB以使用SQL语句添加索引,则需要将索引名称与表上的注释匹配,格式如下:

...
database.execSQL("CREATE UNIQUE INDEX index_tableName_columnName ON tableName (columnName)")
...

不确定索引名称可以有多长,所以不确定使用多列索引时会是什么样子。


低于50声望值的用户无法进行评论,这是有原因的。我认为很明显,发布答案并不能规避此限制。 - Gert Arnold
@GertArnold 我相信这不是问题,我可以写一个完整的答案并加上刚提到的警告,但我个人不喜欢同样的答案被重复多次(特别是当你的答案明显是几周或几个月后),所以我只列出了警告,这可能不能回答原始问题,但它确实与它和其他问题有关。是的,评论也不是用来讨论的。 - Christopher Myers
注意事项并不必要。这是离题了。这里的一切都与生成的迁移代码有关。 - Gert Arnold

0
你还可以通过复合主键实现多列的唯一性(实体通过多个列的组合来唯一标识)。
@Entity(primaryKeys = ["firstName", "lastName"])
data class User(
    val firstName: String?,
    val lastName: String?
)

查看官方文档

请注意,在这种情况下,您不能使用自动生成的id递增,SQLite不支持该功能


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