2010-06-18 87 views
15

我有一个JPA /休眠(3.5.3)的设置,在这里我有一个实体,一个“账户”类,它有子实体,“联系人”实例的列表麻烦。我想能够添加/删除联系人的情况下,进入名单<联系人账户的>财产。休眠触发约束违规使用orphanRemoval

添加一个新的实例进入设置和调用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)); 

的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:

我有,似乎工作的解决方案,但包括使用

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; 
} 

由于休眠CascadeType.DELETE_ORPHAN已过时,我不得不在Hibernate API假设它已经被JPA2版本所取代,但是实现缺乏一些东西。

+0

我认为是什么让这件事情起作用,@JoinColumn上的选项nullable = false,@ManyToOne上的选项= false,而不是自定义的hibernate级联。 – Thierry 2010-06-18 13:21:22

+0

即使我添加了可空和可选标志,它仍然失败,但是这次它意识到它创建了一个它违反了注释约束的场景,而不是SQL。看起来,3.5.3和JPA2可能仍然是一些问题,因为我无法让@ElementCollection表现得很好。 – ptomli 2010-06-18 13:30:06

+0

将CascadeType设置为ALL将执行所有不需要显式指定的操作DELETE_ORPHAN – 2013-04-17 12:57:02

回答

19

一些言论:

  • 既然你有一个双向关联,您需要添加一个mappedBy属性来声明关联的拥有方。
  • 也不要忘记,你需要双向协会工作时管理链接的两侧,我建议使用此方法,防守(如下图所示)。
  • 你必须实现在ContactequalshashCode

所以,在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

@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进行测试。

+0

看来,在我为获取Hibernate特定API工作所做的更改中,我解决了打破JPA2版本的所有操作。有点令人尴尬,我怀疑这是equals/hashCode ... – ptomli 2010-06-21 08:10:31

+0

你好先生,我没有使用'EntityManager',我正在使用'Session#saveOrUpdate'特定的冬眠,在数据库中我有** 3个孩子**,从视图我** ** 2 **孩子**更新,并希望删除**第三或最后一个**,如何处理这种情况?我应该使用'Session#get()'还是'Session#load()'来知道要删除哪个元素? – 2018-02-26 08:01:59