我有一个带有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
属性?
答案 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。