Hibernate使用orphanRemoval触发约束违规

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

将新实例添加到集合中并调用saveOrUpdate(account)可以保持一切可爱。 如果我然后选择从列表中删除联系人并再次调用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) ); 

Java的:

 @Entity class Account { @Id @Column public Long id; @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "account_id") public List 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 contacts = new HashSet(); } class Contact { @ManyToOne(optional = false) @JoinColumn(name = "account_id", nullable = false) private Account account; } 

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

一些评论:

  • 由于您具有双向关联,因此需要添加mappedBy属性以声明关联的拥有方。
  • 另外,请注意,在处理双向关联时,您需要管理链接的两端,我建议使用防御方法(如下所示)。
  • 并且您必须在Contact上实现equalshashCode

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

 @Entity public class Account { @Id @GeneratedValue public Long id; @OneToMany(cascade = CascadeType.ALL, mappedBy = "account", orphanRemoval = true) public List contacts = new ArrayList(); 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 ,重要的是@ManyToOne字段应该将optional标志设置为false

 @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()); 

正如预期的那样,孤立的Contact被删除了。 测试了Hibernate 3.5.3-Final。