我在使用JPA / Hibernate(3.5.3)设置时遇到麻烦,在这里我有一个实体,即“ Account”类,该类具有子实体列表,“ Contact”实例。我试图能够将Contact的实例添加/删除到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<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版本所取代,但是该实现缺少一些东西。
一些说明:
mappedBy
equals
hashCode
Contact
因此,在中Account,像这样修改映射:
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,重要的部分是该@ManyToOne字段应将optional标志设置为false:
@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测试。