Can an INITIALLY DEFERRED constraint be defined using a Hibernate annotation?

谁说我不能喝 提交于 2019-12-24 15:56:12

问题


I have a table with a column that has a UNIQUE constraint. I want the constraint checking to be deferred to commit time.

If I create it using Postgres SQL like this (many columns omitted):

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
)

Then everything works as expected.

If I define it to Hibernate like this:

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 + "]";
    }
}

And use hibernate.hbm2ddl create to create the table, the INITIALLY DEFERRED option is not specified for the name's UNIQUE constraint (as expected because I don't know how to ask for it.)

When I run the app bad things happen.

In particular the user is allowed to swap names between two instruments. If he tries to swap the names between inst1 and inst2 it throws an exception:

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

So the question is: Is there an Hibernate annotation that can be used to specify the INITIALLY DEFERRED property of a constraint on a column?

PS: I'm not looking for a work-around. I have an extra step in the install/setup process that applies the constraint. What I'm hoping for is a way to eliminate that extra step.


回答1:


Unfortunately, Hibernate does not have support for deferred constraints. https://hibernate.atlassian.net/browse/HHH-2248

You could try playing with entityManager.flush() method, let's say you have Instruments with names inst1 and 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");

Alternatively you could get the entities from DB, delete them from DB, perform flush and persist updated entities. This way you don't have to make up the third name.

Not sure about performance effect of those solutions, you have to figure out yourself.



来源:https://stackoverflow.com/questions/47317706/can-an-initially-deferred-constraint-be-defined-using-a-hibernate-annotation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!