Java 使用孤立删除休眠触发约束冲突

Java 使用孤立删除休眠触发约束冲突,java,hibernate,orm,jpa,jpa-2.0,Java,Hibernate,Orm,Jpa,Jpa 2.0,我在JPA/Hibernate(3.5.3)设置中遇到了问题,我有一个实体,一个“Account”类,它有一个子实体列表,“Contact”实例。我正在尝试将联系人的实例添加/删除到Account的列表属性中 在集合中添加一个新实例并调用saveOrUpdate(account)会保持不变。如果我随后选择从列表中删除联系人并再次调用saveOrUpdate,那么SQL Hibernate似乎会将account\u id列设置为null,这违反了数据库约束 我做错了什么 下面的代码显然是一个简化的

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

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

我做错了什么

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

SQL:

爪哇:

编辑#1:

可能这实际上是一个bug

编辑#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;
}
类帐户{
@抑制警告(“弃用”)
@OneToMany(cascade=CascadeType.ALL,mappedBy=“account”)
@Cascade(org.hibernate.annotations.CascadeType.DELETE\u孤儿)
@JoinColumn(name=“account\u id”,nullable=false)
private Set contacts=new HashSet();
}
班级联系{
@多通(可选=假)
@JoinColumn(name=“account\u id”,nullable=false)
私人帐户;
}
由于Hibernate CascadeType.DELETE\u ORPHAN已被弃用,我不得不假设它已被JPA2版本取代,但实现中缺少一些东西。

一些备注:

  • 由于您具有双向关联,因此需要添加
    mappedBy
    属性来声明关联的拥有方
  • 另外,不要忘记,在处理双向关联时,您需要管理链接的两侧,我建议为此使用防御方法(如下所示)
  • 您必须在
    联系人
    上实现
    等于
    哈希代码
因此,在
帐户中
修改映射,如下所示:

@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
}
通过这些修改,以下功能可以正常工作:

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

而孤立的
联系人
会按预期被删除。使用Hibernate 3.5.3-Final进行测试。

我认为是@JoinColumn上的选项nullable=false和@manytone上的选项optional=false,而不是自定义Hibernate级联。即使我添加了nullable和optional标志,它仍然失败,但这一次,当它意识到它已经创建了一个场景,它违反了一个带注释的约束,而不是SQL约束。3.5.3和JPA2似乎仍然存在一些问题,因为我也无法让@ElementCollection表现得很好。将CascadeType设置为ALL将执行所有操作,在我的例子中,您不必显式为DELETE_,我只使用从父级到chhild的单向关系,OrphanRemoving=true,nullable=false这就是它的工作原理。若我删除nullable=false,则在尝试将引用字段更新为null时触发约束冲突失败。如果我删除ophanRemoval,它不会执行任何更新或删除@Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)不是必需的,似乎在我为使特定于hibernate的API正常工作所做的更改中,我解决了破坏JPA2版本的任何问题。有点尴尬的是,我怀疑是equals/hashCode…您好,先生,我没有使用
EntityManager
,我使用的是
Session#saveOrUpdate
hibernate-specific,在DB中我有3个孩子,从视图来看,我有2个孩子要更新,我想删除第三个或最后一个,如何处理这种情况?我应该使用
Session#get()
还是
Session#load()
,来知道要删除哪个元素?
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;
}
@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
}
@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());