May 17, 2005

Hibernate Mappings & Encapsulated Collections

Collections must be encapsulated. Also I am using Hibernate as the persistence solution. I have a uni-directional many to many relationship between Parent and Child. My java code looks like this:

Parent {
String name;
Set children = new HashSet();

public void getChildren() {
return Collections.unmodifiableCollection(children);
}

private void setChildren(Set children) {
this.children = children;
}

public void addChild(Child obj) {
///.....
}

public void removeChild(Child obj) {
///....
}
}

Child {
String name;
//......
}

Now I have hibernate mapping like this:

For parent:

set name="children" table="ParentChildren"
key column="parent_child_id"
many-to-many column="child_id" class="Child"


Hibernate code:

session.createCriteria(Parent.class).list()

When I execute above code something funny happens:All join table records are deleted and re-inserted.

The problem after digging through HIbernate documentation was this:

You must return exactly the same collection that Hibernate sets else it will try to synchronize it with database. As such it deleted and inserted as I was returnign a read only collection, which was different from what I got from hibernate.

Now if I pass a collection thats same I end up violating encapsulation.

To my set I added inverse="true" attribute

set name="children" inverse="true"

But this is an incorrect solution that works!. If you look up the definiton of inverse it means:

  1. Inverse signifies a bi-direction association
  2. Inverse = true says that this end is not responsible for association maintenance.

As such inverse = true stopped the inserts/deletes. But then mine was a uni-direcitional relationship and this was wrong. Because as of now I am only reading the records its fine. But for manipulating children, inverse=true wont work. Becasue whatever I do with child collection wont be persisted to the db.

The challenge was thus to achieve both: encapsulation of collection and proper usage of hibernate.

The solution looks like this:

Parent {
private Set getChildren() {
return this.children; //same as what hibernate sets..make it private
}
public Iterator children() {
return this.children.iterator();
//now clients cant modify the collection without invokding add/remove methods
}
}


Also the mapping remains the same as originally..viz without the inverese attribute because this is unidirectional association. Also semantically this is correct because now children manipulation will be persisted to the database.

Solution summary:

  1. Make your get set methods on a collection private
  2. Add a method that returns an iterator to your collection
  3. Have add/remove encapsulation mehtods on your collection
  4. Look up hibernate docs to specify your mappings based upon if its unidirectional/bidirecitonal association.
  5. Remeber that cascade and inverse are orthogonal concepts.

The following links will make it all clear:

May 15, 2005

My Desk

When its so tidy..chances are I may not be at work :-)) Posted by Hello

May 11, 2005

Using Agile Practices

The past 3 yrs or so I have learnt a lot of lessons, most of which are "how to write better software". ...

The challenge in front of me is to, at every given opportunity (with a change of perception I realize these are infinite) use agile practices.

Why do I want to use agile practices?
  • Most importantly using agile practices makes my life less stressful and work infinte fun :-)

On the whole I have decided to do this:
  • Use automated testing to whatever extent possible
  • Use fully automated builds
  • Tests should be of different types: unit, functional, peformance to say the least
  • Refactor (but only after giving it a thought)
Let me see how much I succeed.