Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mapping a bidirectional list with Hibernate

I don't understand the behavior of Hibernate when mapping a bidirectional list. The SQL statements that Hibernate produces seem not optimal to me. Can somebody enlighten me?

The scenario is the following: I have a one-to-many parent-child relationship. I map this relationship with a bidirectional list.

According to the Hibernate Annotation Reference Guide (Chapter: Bidirectional association with indexed collections) the mapping should look like this:

@Entity
public class Parent {

    @Id  @GeneratedValue private long id;
    @Version  private int version;
    private String name;

    @OneToMany(cascade = CascadeType.ALL)
    @JoinColumn(name = "parent_id", nullable=false)
    @org.hibernate.annotations.IndexColumn(name = "parent_index")
    List<Child> children = new ArrayList<Child>();

...

@Entity
public class Child {

    @Id @GeneratedValue private Long id;
    @Version private int version;
    private String name;

    @ManyToOne
    @JoinColumn(name = "parent_id", updatable = false, insertable = false, nullable=false)
    private Parent parent;

...

But in this case Hibernate produces three SQL statements when persisting a parent with one child:

Hibernate: insert into Parent (name, version, id) values (?, ?, ?)
Hibernate: insert into Child (name, price, version, parent_id, parent_index, id) values (?, ?, ?, ?, ?, ?)
Hibernate: update Child set parent_id=?, parent_index=? where id=?

The third statement seems to be redundant, because parent_id and parent_index seem to be set already in the second statement.

When I change the mapping and repeat the attributes 'updatable = false, insertable = false' to the declaration of the @JoinColumn in the Parent like this:

@Entity
public class Parent {

    @Id  @GeneratedValue private long id;
    @Version  private int version;
    private String name;

    @OneToMany(cascade = CascadeType.ALL)
    @JoinColumn(name = "parent_id", updatable = false, insertable = false, nullable=false)
    @org.hibernate.annotations.IndexColumn(name = "parent_index")
    List<Child> children = new ArrayList<Child>();

...

@Entity
public class Child {

    @Id @GeneratedValue private Long id;
    @Version private int version;
    private String name;

    @ManyToOne
    @JoinColumn(name = "parent_id", updatable = false, insertable = false, nullable=false)
    private Parent parent;

...

...then Hibernate seems to produce much more optimized SQL:

Hibernate: insert into Parent (name, version, id) values (?, ?, ?)
Hibernate: insert into Child (name, price, version, parent_id, parent_index, id) values (?, ?, ?, ?, ?, ?)

The client code looks like this:

    EntityManagerFactory emf = Persistence.createEntityManagerFactory("test");
    EntityManager em = emf.createEntityManager();
    EntityTransaction tx = em.getTransaction();
    tx.begin();

    Parent newParent = new Parent();
    newParent.setName("Parent1");

    Child newChild = new Child();
    newChild.setName("Child1");

    newParent.getChildren().add(newChild);
    newChild.setParent(newParent);

    em.persist(newParent);

    em.flush();
    tx.commit();

I am using hibernate-entitymanager 3.4.0.GA.

What am I missing? Is the Hibernate Reference Guide not correct, or am I overlooking something?

like image 820
jbandi Avatar asked Feb 02 '09 23:02

jbandi


People also ask

What is bidirectional mapping in hibernate?

Bidirectional association allows us to fetch details of dependent object from both side. In such case, we have the reference of two classes in each other. Let's take an example of Employee and Address, if Employee class has-a reference of Address and Address has a reference of Employee.

What is unidirectional and bidirectional in Hibernate mapping?

A bidirectional relationship has both an owning side and an inverse side. A unidirectional relationship has only an owning side. The owning side of a relationship determines how the Persistence runtime makes updates to the relationship in the database.

What is necessary for maintaining a bidirectional relationship in a many-to-many relationship in hibernate?

You just need to reference the name of the association attribute of the many side as the value of the mappedBy attribute and Hibernate has all the information it needs. That's all you need to do to define a bidirectional many-to-one association.


1 Answers

Ok, I was not reading the Annotations Reference Guide thoroughly enough.

In Chapter 2.2.5.3.1.1. Bidirectional it is stated clearly:

To map a bidirectional one to many, with the one-to-many side as the owning side, you have to remove the mappedBy element and set the many to one @JoinColumn as insertable and updatable to false. This solution is obviously not optimized and will produce some additional UPDATE statements.

It probably would not hurt to repeat this information in Chapter 2.4.6.2.1. Bidirectional association with indexed collections .

Now the question remains: if I repeat the @JoinColumn attributes 'updatable = false' and 'insertable = false' on the Parent (see code in first post) the additional update statements seem not to get produced... is this a legitimate workaround? Or does this result in another problem?

like image 195
jbandi Avatar answered Sep 29 '22 17:09

jbandi