Skip navigation

Monthly Archives: March 2009

I’ve been working recently on a project that heavily relies on Hibernate.  I’ve learned a ton about Hibernate in the past few months.  Hibernate is excellent, it really saves a ton of time when it comes to dealing with data object persistence.  However, it can be difficult sometimes to get it to work exactly the way you want it to.

Some of the more advanced mappings are not quite as simple as the documentation, and even the excellent book Java Persistence with Hibernate will have you believe.  For instance, mapping Map collections properly.  The cleanest way to manage a collection of child objects is like this:

Parent parent = new Parent();

Child child = new Child();

parent.getChildren().put(“someChild”, child);

HibernateUtil.getSession().save(parent);

This works very well (with cascade = all) by default.  In order for the above example to work, the collection has to be mapped with the “inverse = false”.  The inverse flag is a matter of some confusion.  It simply tells Hibernate who will control the relationship between the parent and the child.  If inverse = false, the parent is the master, if it is true the child is the master.  This means, if the child is the master, the parent property MUST be set or the relationship will not be persisted, even if you add the child to the parent’s children collection.  If the parent is the master, the object must be added to its children collection, or the relationship will not be persisted.  Underneath it all, the inverse flag manages how the queries during inserts and updates are written.

The problem occurs when deletion of children needs to be managed as well.  Add this bit of code to the above example:

parent.getChildren().remove(“someChild”);

HibernateUtil.getSession().save(parent);

In order for this to work, the cascade option needs to be set to “all-delete-orphan.”  This instructs Hibernate to delete the children if they no longer belong to the parent.  An important thing to note is: if the collection is mapped with inverse=”true” all-delete-orphan DOES NOT DO ANYTHING.  This is something that I did not see in my reading (although I’m sure it’s there somewhere).  When the collection is mapped with inverse = true, you need to set the child’s parent property to null, or manually delete the object.

If you are using inverse=false, you may run into a weird issue with all-delete-orphan.  If your child’s parent foreign key relationship is set to not allow null values in the database, you will receive errors like “ParentFK cannot be null.”  This is because prior to deleting the child object Hibernate executes a query like this: “update Children set ParentFK = null where ChildID = 99;” .  Why it does this is beyond me.  However I do know how to fix the issue.  What you have to do is set the map’s key column to not null, and set update = true.  Then, in the child mapping, set the parent many to one to not insert or update.  You will need to do the same for the index of the map mapping.  Here’s what the mappings should look like:

<map name=”children” cascade=”all-delete-orphan” inverse=”false”>

<key column=”ParentFK” not-null=”true” update=”true” />

<index column=”Name” type=”string” update=”true” />

<element type=”Child” />

</map>

The child’s parent property should look like this:

<many-to-one name=”parent” column=”ParentFK” class=”Parent” update=”false” insert=”false” />

<property name=”name” column=”Name” updated=”false” insert=”false” />

This will get rid of the nasty issue of Hibernate trying to update the foreign key to null before it deletes children.  Another problem I ran into with map collections is when the index property for the child object did not exist on the child iteself, but on an object that the child references.  To fix this, all you need to do is change the index to a map-key, and use a formula like so:

<map name=”children” cascade=”all-delete-orphan” inverse=”false”>

<key column=”ParentFK” not-null=”true” update=”true” />

<map-key column=”o.Name” type=”string” formula=”(select o.Name from Catalog.OtherObjects o where o.OtherObjectID = OtherObjectFK)” />

<element type=”Child” />

</map>

Notice I included the catalog name before the table name, this is required if you do not select a default database in your hibernate configuration (I do this because we have multiple databases which our objects are stored in).

Well, hopefully these findings are helpful to others.  I’m sure they are not original, but I had a hard time digging this information up online.  As a matter of fact I did not find this information anywhere out there surprisingly.  I had to piece it together by going through the documentation and running unit tests.

Advertisements