代码之家  ›  专栏  ›  技术社区  ›  ptomli

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

  •  15
  • ptomli  · 技术社区  · 14 年前

    我在JPA/Hibernate(3.5.3)的设置中遇到了问题,我有一个实体,一个“Account”类,它有一个子实体列表,“Contact”实例。我正在尝试将联系人实例添加/删除到列表中<联系方式>账户财产。

    将一个新实例添加到集合中并调用saveOrUpdate(account)会保持一切。如果然后选择从列表中删除联系人并再次调用saveOrUpdate,那么SQL Hibernate似乎会将account\u id列设置为null,这违反了数据库约束。

    我做错什么了?

    下面的代码显然是一个简化的抽象,但我认为它涵盖了这个问题,因为我在不同的代码中看到了相同的结果,这确实是关于这个简单的问题。

    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:

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

    编辑#2:

    我有一个似乎可行的解决方案,但涉及到使用hibernateapi

    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\u ORPHAN已被弃用,我不得不假设它已被JPA2版本取代,但实现中缺少一些东西。

    1 回复  |  直到 14 年前
        1
  •  21
  •   Pascal Thivent    14 年前

    一些评论:

    • mappedBy
    • 另外,不要忘记,在使用双向关联时,您需要管理链接的两侧,我建议对此使用防御方法(如下所示)。
    • 你必须实施 equals hashCode Contact .

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

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

    联系人