Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cascading save not working with Hibernate 4 and @OneToMany

I've been having an issue with Hibernate 4 that did not happen in Hibernate 3. I'm testing a base dao class which has generic methods for saving, querying, etc.. (not it's not homework). I'm using an embedded derby connection with hibernate.hbm2ddl.auto=create-drop for testing purposes.

EDIT: Fixing cascadeType. Still the same behavior. Even if i add in all the available cascade types from the CascadeType enum, it still fails, unless I put CascadeType.ALL. Not sure what ALL has that you can't specifically enumerate.

In my tests, I have following mapped classes:

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

And then i try to save:

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

I get the following trace:

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)
...

I noticed the following sql being executed:

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

It seems it's trying to insert into the collection before cascading the save down to the FooChild object.

If i change FooParent to have this:

@OneToMany(cascade={CascadeType.ALL})

The problem goes away.

As i said, this works with hibernate 3, so i'm not sure why it's behaving differently.

like image 265
Matt Avatar asked Jun 28 '12 19:06

Matt


1 Answers

save(), persist() and merge() are three different operations. If you want to cascade the save() operation, you need to use the Hibernate annotation

@Cascade(CascadeType.SAVE_UPDATE)

or

cascade = CascadeType.ALL

, which also include the Hibernate proprietary operations.

like image 155
JB Nizet Avatar answered Oct 15 '22 17:10

JB Nizet