[英]Hibernate triggering constraint violations using orphanRemoval
I'm having trouble with a JPA/Hibernate (3.5.3) setup, where I have an entity, an "Account" class, which has a list of child entities, "Contact" instances. 我在使用JPA / Hibernate(3.5.3)设置时遇到问题,我有一个实体,一个“帐户”类,它有一个子实体列表,“联系”实例。 I'm trying to be able to add/remove instances of Contact into a List<Contact> property of Account. 我正在尝试将Account的实例添加/删除到Account的List <Contact>属性中。
Adding a new instance into the set and calling saveOrUpdate(account) persists everything lovely. 将新实例添加到集合中并调用saveOrUpdate(account)可以保持一切可爱。 If I then choose to remove the contact from the list and again call saveOrUpdate, the SQL Hibernate seems to produce involves setting the account_id column to null, which violates a database constraint. 如果我然后选择从列表中删除联系人并再次调用saveOrUpdate,则SQL Hibernate似乎会产生涉及将account_id列设置为null,这违反了数据库约束。
What am I doing wrong? 我究竟做错了什么?
The code below is clearly a simplified abstract but I think it covers the problem as I'm seeing the same results in different code, which really is about this simple. 下面的代码显然是一个简化的摘要,但我认为它涵盖了问题,因为我在不同的代码中看到相同的结果,这真的是这个简单。
SQL: SQL:
CREATE TABLE account ( INT account_id );
CREATE TABLE contact ( INT contact_id, INT account_id REFERENCES account (account_id) );
Java: Java的:
@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);
Result: 结果:
UPDATE contact SET account_id = null WHERE contact_id = ?
Edit #1: 编辑#1:
It might be that this is actually a bug http://opensource.atlassian.com/projects/hibernate/browse/HHH-5091 可能这实际上是一个错误http://opensource.atlassian.com/projects/hibernate/browse/HHH-5091
Edit #2: 编辑#2:
I've got a solution that seems to work, but involves using the Hibernate API 我有一个似乎有效的解决方案,但涉及使用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;
}
Since Hibernate CascadeType.DELETE_ORPHAN is deprecated, I'm having to assume that it has been superseded by the JPA2 version, but the implementation is lacking something. 由于不推荐使用Hibernate CascadeType.DELETE_ORPHAN,我不得不假设它已被JPA2版本取代,但实现缺乏某些功能。
Some remarks: 一些评论:
mappedBy
attribute to declare the owning side of the association. 由于您具有双向关联,因此需要添加mappedBy
属性以声明关联的拥有方。 equals
and hashCode
on Contact
. 并且您必须在Contact
上实现equals
和hashCode
。 So, in Account
, modify the mapping like this: 因此,在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
}
In Contact
, the important part is that the @ManyToOne
field should have the optional
flag set to false
: 在Contact
,重要的是@ManyToOne
字段应该将optional
标志设置为false
:
@Entity
public class Contact {
@Id @GeneratedValue
public Long id;
@ManyToOne(optional = false)
public Account account;
// getters, setters, equals, hashCode
}
With these modifications, the following just works: 通过这些修改,以下工作正常:
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());
And the orphaned Contact
gets deleted, as expected. 正如预期的那样,孤立的Contact
被删除了。 Tested with Hibernate 3.5.3-Final. 经过Hibernate 3.5.3-Final测试。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.