级联保存不适用于 Hibernate 4 和 @OneToMany

Posted

技术标签:

【中文标题】级联保存不适用于 Hibernate 4 和 @OneToMany【英文标题】:Cascading save not working with Hibernate 4 and @OneToMany 【发布时间】:2012-06-30 09:25:53 【问题描述】:

我在 Hibernate 4 中遇到了一个在 Hibernate 3 中没有发生的问题。我正在测试一个基础 dao 类,它具有用于保存、查询等的通用方法。(不是作业)。我正在使用带有 hibernate.hbm2ddl.auto=create-drop 的嵌入式 derby 连接进行测试。

编辑:修复 cascadeType。仍然是相同的行为。即使我从 CascadeType 枚举中添加了所有可用的级联类型,它仍然会失败,除非我输入 CascadeType.ALL。不确定 ALL 有什么你无法具体列举的。

在我的测试中,我有以下映射类:

@Entity
@Table(name="FOO_CHILD")
public class FooChild 
    @Id
    @Column(name="FOO_ID")
    private Long id;

    @Column(name="NAME")
    private String name;

    public Long getId() 
        return id;
    

    public void setId(Long id) 
        this.id = id;
    

    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
    


@Entity
@Table(name="FOO_PARENT")
public class FooParent 
    @Id
    @Column(name="FOO_PARENT_ID")
    private Long id;

    @Column(name="NAME")
    private String name;

    @Fetch(FetchMode.SELECT)
    @Type(type="long")
    @OneToMany(cascade=CascadeType.PERSIST, CascadeType.MERGE)
    @JoinTable(name="FOO_PARENT_FOO",
            joinColumns=@JoinColumn(referencedColumnName="FOO_PARENT_ID"),
            inverseJoinColumns=@JoinColumn(referencedColumnName="FOO_ID"))
    private List<Foo> fooChild;

    public Long getId() 
        return id;
    

    public void setId(Long id) 
        this.id = id;
    

    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
    

    public List<Foo> getFooChild() 
        return fooChild;
    

    public void setFooChild(List<Foo> fooChild) 
        this.fooChild = fooChild;
    

然后我尝试保存:

FooChild fooChild = new FooChild();
fooChild.setId(1L);
fooChild.setName("fooChild");

FooParent fooParent = new FooParent();
fooParent.setId(1L);
fooParent.setName("fooParent");
fooParent.setFooChild(new ArrayList<FooChild>(Arrays.asList(fooChild)));

session.save(fooParent);
session.flush();
session.clear();

我得到以下跟踪:

org.hibernate.exception.ConstraintViolationException: INSERT on table 'FOO_PARENT_FOO' caused a violation of foreign key constraint 'FKF18290CAECA19CB8' for key (1).  The statement has been rolled back.
    at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:74)
...

我注意到正在执行以下 sql:

Hibernate: 
    insert 
    into
        foo_parent
        (name, foo_parent_id) 
    values
        (?, ?, ?, ?)
Hibernate: 
    insert 
    into
        foo_parent_foo
        (foo_parent, foo_child) 
    values
        (?, ?)

它似乎试图在将保存级联到 FooChild 对象之前插入到集合中。

如果我将 FooParent 更改为:

@OneToMany(cascade=CascadeType.ALL)

问题消失了。

正如我所说,这适用于 hibernate 3,所以我不确定为什么它的行为不同。

【问题讨论】:

cascade=CascadeType.MERGE, CascadeType.MERGE: 这里没有 SAVE_UPDATE 的级联。 抱歉,我尝试了几件事,但在我恢复的内容中有错误。它应该是 CascadeType.PERSIST, CascadeType.MERGE 【参考方案1】:

save()persist()merge() 是三种不同的操作。如果想级联save()操作,需要使用Hibernate注解

@Cascade(CascadeType.SAVE_UPDATE)

cascade = CascadeType.ALL

,其中还包括 Hibernate 的专有操作。

【讨论】:

谢谢。一般来说,我尽量避免使用特定于休眠的注释。尽管 JPA 规范相当不完整,但这是我的普遍看法。更不用说 JPA API 比 hibernate 更难使用。尤其是 CriteriaBuilder。 如果你想避免 Hibernate 特有的注解(我能理解),你也应该避免 Hibernate 特有的方法,例如 save()、saveOrUpdate() 和 update()。坚持persist()merge(),你就不需要Hibernate 特定的注解了。

以上是关于级联保存不适用于 Hibernate 4 和 @OneToMany的主要内容,如果未能解决你的问题,请参考以下文章

hibernate第三天

Hibernate Cascade

OneToMany Hibernate 保存级联问题

Hibernate 4 字节码增强不适用于脏检查优化

Hibernate 4.3.5 不适用于 Oracle10g 数据库

hibernate---级联保存级联删除