一、简介

在本教程中,我们将讨论使用 JPA 和 Hibernate 定义唯一约束

首先,我们将探讨独特的约束以及它们与主键约束有何不同。

然后我们来看看JPA的重要注解@Column (unique=true)@UniqueConstraint。 我们将实现它们来定义单列和多列的唯一约束。

最后,我们将学习如何对引用的表列定义唯一约束。

2. 独特的限制

让我们先快速回顾一下。唯一键是表中唯一标识数据库表中的记录的一组单列或多列。

唯一约束和主键约束都为一列或一组列的唯一性提供了保证。

2.1.它与主键约束有何不同?

唯一约束确保列或列组合中的数据对于每行都是唯一的。例如,表的主键充当隐式唯一约束。因此, 主键约束自动具有唯一约束。

此外,每个表只能有一个主键约束。但是,每个表可以有多个唯一约束。
简而言之,除了主键映射带来的任何约束之外,还应用唯一约束。

我们定义的唯一约束在表创建期间用于生成正确的数据库约束,也可以在运行时用于排序 insertupdatedelete 语句。

2.2.什么是单列和多列约束?

唯一约束可以是列约束或表约束。在表级别,我们可以定义跨多个列的唯一约束。

JPA 允许我们使用 @Column(unique=true)@UniqueConstraint 在代码中定义唯一约束。 这些注释由模式生成过程解释,自动创建约束。

首先,我们应该强调 列级约束适用于单个列,表级约束适用于整个表。

我们将在下一节中更详细地讨论这一点。

3. 设置实体

JPA 中的实体表示存储在数据库中的表。实体的每个实例代表表中的一行。

让我们首先创建一个域实体并将其映射到数据库表。对于此示例,我们将创建一个 Person 实体:

@Entity
@Table
public class Person implements Serializable {
    @Id
    @GeneratedValue
    private Long id;  
    private String name;
    private String password;
    private String email;
    private Long personNumber;
    private Boolean isActive;
    private String securityNumber;
    private String departmentCode;
    @JoinColumn(name = "addressId", referencedColumnName = "id")
    private Address address;
   //getters and setters
 }

地址 字段是来自 Address 实体的引用字段:

@Entity
@Table
public class Address implements Serializable {
    @Id
    @GeneratedValue
    private Long id;
    private String streetAddress;
    //getters and setters
}

在本教程中,我们将使用此 Person 实体来演示我们的示例。

4. 列约束

当我们准备好模型后,我们就可以实现第一个唯一约束。

让我们考虑一下保存人员信息的 Person 实体。我们有 id 列的主键。该实体还保存 PersonNumber, 它不包含任何重复值。另外,我们无法定义主键,因为我们的表已经有了它。

在这种情况下,我们可以使用列唯一约束来确保在 PersonNumber 字段中不会输入重复的值。 JPA 允许我们使用带有 unique 属性的 @Column 注释来实现这一点。

在下面的部分中,我们将了解 @Column 注释,然后学习如何实现它。

4.1. @Column(唯一= true)

注释类型 Column 用于指定持久属性或字段的映射列。

我们看一下定义:

@Target(value={METHOD,FIELD})
@Retention(value=RUNTIME)
public @interface Column {
    boolean unique;
   //other elements
 }

unique 属性指定该列是否是唯一键。这是 UniqueConstraint 注释的快捷方式,当唯一键约束仅对应于单个列时非常有用。

我们将在下一节中了解如何定义它。

4.2.定义列约束

每当唯一约束仅基于一个字段时,我们就可以对该列使用 @Column(unique=true)

让我们在 personNumber 字段上定义一个唯一约束:

@Column(unique=true)
private Long personNumber;

当我们执行模式创建过程时,我们可以从日志中验证它:

[main] DEBUG org.hibernate.SQL -
    alter table Person add constraint UK_d44q5lfa9xx370jv2k7tsgsqt unique (personNumber)

同样,如果我们想限制一个 使用唯一的电子邮件注册,我们可以在 电子邮件 字段上添加唯一约束:

@Column(unique=true)
private String email;

让我们执行模式创建过程并检查约束:

[main] DEBUG org.hibernate.SQL -
    alter table Person add constraint UK_585qcyc8qh7bg1fwgm1pj4fus unique (email)

虽然当我们想要对单个列施加唯一约束时这很有用,但有时我们可能想对复合键(即列的组合)添加唯一约束。要定义复合唯一键,我们可以使用表约束。我们将在下一节中讨论这个问题。

5. 表约束

复合唯一键是由列组合而成的唯一键。 要定义复合唯一键,我们可以在表而不是列上添加约束。 JPA 使用 @UniqueConstraint 注释帮助我们实现这一目标。

5.1. @UniqueConstraint 注解

注释类型 UniqueConstraint 指定将在为表生成的 DDL(数据定义语言)中包含唯一约束。

我们看一下定义:

@Target(value={})
@Retention(value=RUNTIME)
public @interface UniqueConstraint {
    String name() default "";
    String[] columnNames();
}

我们可以看到, * StringString[] 类型的 namecolumnNames 分别是可以为 UniqueConstraint 注解指定的注解元素。 *

我们将在下一节中通过示例更好地了解每个参数。

5.2.定义唯一约束

让我们考虑一下我们的 Person 实体。一个 不应该有任何活动状态的重复记录。换句话说,由 personNumberisActive 组成的键不会有任何重复值。在这里,我们需要添加跨多个列的唯一约束。

JPA 通过 @UniqueConstraint 注释帮助我们实现这一目标。我们在 uniqueConstraints 属性下的 @Table 注释中使用它。让我们记住指定列的名称:

@Table(uniqueConstraints = { @UniqueConstraint(columnNames = { "personNumber", "isActive" }) })

生成模式后我们可以验证它:

[main] DEBUG org.hibernate.SQL -
    alter table Person add constraint UK5e0bv5arhh7jjhsls27bmqp4a unique (personNumber, isActive)

这里需要注意的一点是,如果我们不指定名称,那么它就是提供者生成的值。 从 JPA 2.0 开始,我们可以为我们的唯一约束提供一个名称:

@Table(uniqueConstraints = { @UniqueConstraint(name = "UniqueNumberAndStatus", columnNames = { "personNumber", "isActive" }) })

我们可以验证同样的结果:

[main] DEBUG org.hibernate.SQL -
    alter table Person add constraint UniqueNumberAndStatus unique (personNumber, isActive)

在这里,我们对一组列添加了唯一约束。我们还可以添加多个唯一约束,这意味着对多组列的唯一约束。我们将在下一节中这样做。

5.3.单个实体上的多个唯一约束

一个表可以有多个唯一约束。 在上一节中,我们定义了组合键的唯一约束: personNumberisActive 状态。在本节中,我们将添加对 securityNumberDepartmentCode 组合的约束。

让我们收集我们的唯一索引并立即指定它们。我们通过在大括号中重复 @UniqueConstraint 注释并用逗号分隔来实现此目的:

@Table(uniqueConstraints = {
   @UniqueConstraint(name = "UniqueNumberAndStatus", columnNames = {"personNumber", "isActive"}),
   @UniqueConstraint(name = "UniqueSecurityAndDepartment", columnNames = {"securityNumber", "departmentCode"})})

现在让我们查看日志并检查约束:

[main] DEBUG org.hibernate.SQL -
    alter table Person add constraint UniqueNumberAndStatus unique (personNumber, isActive)
[main] DEBUG org.hibernate.SQL -
   alter table Person add constraint UniqueSecurityAndDepartment unique (securityNumber, departmentCode)

到目前为止,我们对同一实体中的字段定义了唯一的约束。然而,在某些情况下,我们可能引用了其他实体的字段,需要确保这些字段的唯一性。我们将在下一节中讨论这个问题。

6. 引用表列的唯一约束

当我们创建两个或多个彼此相关的表时,它们通常通过一个表中引用另一个表的主键的列来关联。该列称为“外键”。例如, PersonAddress 实体通过 addressId 字段连接。因此, addressId 充当引用表列。

我们可以在引用的列上定义唯一约束*。*我们将首先在单个列上实现它,然后在多个列上实现。

6.1.单列约束

在我们的 Person 实体中,我们有一个引用 Address 实体的 地址 字段。一个 应该有一个唯一的地址。

因此,让我们在 Person地址 字段上定义一个唯一约束:

@Column(unique = true)
private Address address;

现在让我们快速检查一下这个约束:

[main] DEBUG org.hibernate.SQL -
   alter table Person add constraint UK_7xo3hsusabfaw1373oox9uqoe unique (address)

我们还可以在引用的表列上定义多个列约束,我们将在下一节中看到。

6.2.多列约束

我们可以对列的组合指定唯一约束。如前所述,我们可以使用表约束来做到这一点。

让我们对 personNumber地址定义唯一约束, 并将其添加到 uniqueConstraints 数组中:

@Entity
@Table(uniqueConstraints = 
  { //other constraints
  @UniqueConstraint(name = "UniqueNumberAndAddress", columnNames = { "personNumber", "address" })})

最后,让我们看看独特的约束:

[main] DEBUG org.hibernate.SQL -
    alter table Person add constraint UniqueNumberAndAddress unique (personNumber, address)

七、结论

唯一约束可防止两个记录在一列或一组列中具有相同的值。

在本文中,我们学习了如何在 JPA 中定义唯一约束。首先,我们回顾一下独特的限制。然后我们讨论了 @Column(unique=true)@UniqueConstraint 注解,分别定义单列和多列的唯一约束。

与往常一样,本文中的示例可以在 GitHub 上找到。