休眠触发约束冲突使用孤立删除

2022-09-03 06:27:21

我在JPA / Hibernate(3.5.3)设置时遇到问题,其中我有一个实体,一个“帐户”类,其中包含子实体列表,“联系人”实例。我正在尝试将联系人的实例添加/删除到“帐户<联系人>属性中。

将新实例添加到集合中并调用 saveOrUpdate(帐户) 可以持久保存所有可爱的东西。如果我随后选择从列表中删除联系人并再次调用 saveOrUpdate,则 SQL Hibernate 似乎会产生将account_id列设置为 null,这违反了数据库约束。

我做错了什么?

下面的代码显然是一个简化的摘要,但我认为它涵盖了这个问题,因为我在不同的代码中看到了相同的结果,这真的很简单。

SQL:

CREATE TABLE account ( INT account_id );
CREATE TABLE contact ( INT contact_id, INT account_id REFERENCES account (account_id) );

爪哇岛:

@Entity
class Account {
  @Id
  @Column
  public Long id;

  @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
  @JoinColumn(name = "account_id")
  public List<Contact> contacts;
}

@Entity
class Contact {
  @Id
  @Column
  public Long id;

  @ManyToOne(optional = false)
  @JoinColumn(name = "account_id", nullable = false)
  public Account account;
}

Account account = new Account();
Contact contact = new Contact();

account.contacts.add(contact);
saveOrUpdate(account);

// some time later, like another servlet request....

account.contacts.remove(contact);
saveOrUpdate(account);

结果:

UPDATE contact SET account_id = null WHERE contact_id = ?

编辑#1:

可能这实际上是一个错误 http://opensource.atlassian.com/projects/hibernate/browse/HHH-5091

编辑#2:

我有一个似乎有效的解决方案,但涉及使用Hibernate API

class Account {
    @SuppressWarnings("deprecation")
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "account")
    @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
    @JoinColumn(name = "account_id", nullable = false)
    private Set<Contact> contacts = new HashSet<Contact>();
}

class Contact {
    @ManyToOne(optional = false)
    @JoinColumn(name = "account_id", nullable = false)
    private Account account;
}

由于Hibernate CascadeType.DELETE_ORPHAN已被弃用,我不得不假设它已被JPA2版本取代,但实现缺少一些东西。


答案 1

一些备注:

  • 由于您具有双向关联,因此需要添加一个属性来声明关联的所属方。mappedBy
  • 另外,不要忘记,在使用双向关联时,您需要管理链接的两端,我建议为此使用防御方法(如下所示)。
  • 并且您必须实现 和 。equalshashCodeContact

因此,在 中,修改映射,如下所示:Account

@Entity
public class Account {
    @Id @GeneratedValue
    public Long id;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "account", orphanRemoval = true)
    public List<Contact> contacts = new ArrayList<Contact>();

    public void addToContacts(Contact contact) {
        this.contacts.add(contact);
        contact.setAccount(this);
    }

    public void removeFromContacts(Contact contact) {
        this.contacts.remove(contact);
        contact.setAccount(null);
    }

    // getters, setters
}

在 中,重要的部分是该字段应将标志设置为:Contact@ManyToOneoptionalfalse

@Entity
public class Contact {
    @Id @GeneratedValue
    public Long id;

    @ManyToOne(optional = false)
    public Account account;

    // getters, setters, equals, hashCode

}

通过这些修改,以下内容将正常工作:

Account account = new Account();
Contact contact = new Contact();

account.addToContact(contact);
em.persist(account);
em.flush();

assertNotNull(account.getId());
assertNotNull(account.getContacts().get(0).getId());
assertEquals(1, account.getContacts().size());

account.removeFromContact(contact);
em.merge(account);
em.flush();
assertEquals(0, account.getContacts().size());

孤儿将被删除,正如预期的那样。使用 Hibernate 3.5.3-Final 进行测试。Contact


答案 2

推荐