可以使用Hibernate注释定义INITIALLY DEFERRED约束吗?

时间:2017-11-15 21:38:17

标签: postgresql hibernate hibernate-annotations

我有一个带有UNIQUE约束的列的表。我希望将约束检查推迟到提交时间。

如果我使用像这样的Postgres SQL创建它(省略了许多列):

CREATE TABLE instrument
(
  id bigint NOT NULL,
  name character varying(255) NOT NULL,
  CONSTRAINT instrument_pkey PRIMARY KEY (id),
  CONSTRAINT instrument_name_key UNIQUE (name)
     DEFERRABLE INITIALLY DEFERRED
)

然后一切都按预期工作。

如果我像这样定义Hibernate:

import java.io.Serializable;
import javax.persistence.*;
import org.hibernate.annotations.ForeignKey;

@Entity
@Table(name="instrument")
public class Instrument implements Versionable, Serializable {
    private static final long serialVersionUID = 1L;

    public static final String NAME_PROPERTY = "name";

    @Version
    private int version;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_GEN")
    private Long id;

    @Column(name="name", unique=true, nullable=false)
    private String name;

    public Instrument() {
       // null constructor to make Hibernate happy
    }

    public Instrument(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(getName());
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Instrument) {
            Instrument other = (Instrument)obj;
            return Objects.equal(getName(), other.getName());
        }
        return false;
    }

    @Override
    public String toString() {
        return "Instrument [id=" + id + ", name=" + name + "]";
    }
}

使用hibernate.hbm2ddl create创建表,没有为名称的UNIQUE约束指定INITIALLY DEFERRED选项(正如预期的那样,因为我不知道如何要求它。)

当我运行应用程序时,会发生不好的事情。

特别是允许用户在两​​个乐器之间交换名称。如果他试图在inst1和inst2之间交换名称,则抛出异常:

org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "instrument_name_key"
  Detail: Key (name)=(inst1) already exists.

所以问题是:是否有一个Hibernate注释可用于指定列上约束的INITIALLY DEFERRED属性?

PS:我不是在寻找解决方法。我在安装/设置过程中有一个额外的步骤来应用约束。我希望的是消除这一额外步骤的方法。

2 个答案:

答案 0 :(得分:1)

不幸的是,Hibernate不支持延迟约束。 https://hibernate.atlassian.net/browse/HHH-2248

您可以尝试使用entityManager.flush()方法,让我们假设您的乐器名称为 inst1 inst2

Instrument inst1 = entityManager.find(Instrument.class, 1);
// change name of first Instrument to some random one
inst1.setName("inst3");
entityManager.flush();
Instrument inst2 = entityManager.find(Instrument.class, 2);
inst2.setName("inst1");
entityManager.flush();
inst1.setName("inst2");

或者,您可以从DB获取实体,从DB中删除它们,执行刷新并保留更新的实体。这样你就不必补上第三个名字了。

不确定这些解决方案的性能效果,你必须弄清楚自己。

答案 1 :(得分:0)

如果您使用 Flyway 或其他数据库版本控制/迁移工具,您可以简单地在定义 Hibernate/JPA 生成的约束的文件之后添加另一个版本 DDL 文件。这个附加文件会将这些约束重新定义为 DEFERRABLE INITIALLY DEFERRED。