Search This Blog

Wednesday, July 16, 2008

More on the equals()/hashCode() Hack with Hibernate

The last blog I did not address Object Retrival. A reader of the blog mentioned the same. Prior to Hibernate 3, I beleive that custom collections could not be supported. However, as of Hibernate 3, they are. R.J.Lorimer's blog on how to implement the same is super!

The use of a the attribute "collection-type" when defining the mapping of the Set is the key to the solution.

So moving forward, for Hibernate, we define a custom CollectionType that factories the PropertyListenerHashSet as shown below:

  1 public class HibernatePropListenerHashSetType implements UserCollectionType {
2 public boolean contains(Object collection, Object entity) {
3 Set set = (Set) collection;
4 return set.contains(entity);
5 }
6
7 public Iterator getElementsIterator(Object collection) {
8 return ((Set) collection).iterator();
9 }
10
11 public Object indexOf(Object collection, Object entity) {
12 return null;
13 }
14
15 public Object instantiate(int anticipatedSize) {
16 return new PropertyListenerHashSet();
17 }
18
19 public PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister) throws HibernateException {
20 return new PersistentSet(session);
21 }
22
23 @SuppressWarnings("unchecked") public Object replaceElements(Object original, Object target,
24 CollectionPersister persister, Object owner, Map copyCache, SessionImplementor session) throws HibernateException {
25 Set setA = (Set) original;
26 Set setB = (Set) target;
27 setB.clear();
28 setB.addAll(setA);
29
30 return null;
31 }
32
33 public PersistentCollection wrap(SessionImplementor session, Object collection) {
34 return new PersistentSet(session, (Set) collection);
35 }
36 }
Pretty simple...now the blocker!! Hibernate does not have an annotation that will support the custom collection-type, for example, something like @CollectionType (name=FooCollection.class). This hibernate forum entry describes the situation. It might be supported at a later date.

In the meanwhile, we can still achieve the custom collection type using hbm mappings :-). So reverting to XML mapping, we defined the Organization object's mapping to use our custom collections as follows:

  1 <set name="applications"  inverse="true" cascade="all-delete-orphan"
2 collection-type="com.welflex.collection.HibernatePropListenerHashSetType">
3 <key column="ORG_ID" not-null="true"/>
4 <one-to-many class="com.welflex.model.SmartApplication" />
5 </set>


The unit test has been augmented to test for the same:

  1     try {
2 session = HibernateUtil.getSessionFactory().getCurrentSession();
3 tx = session.beginTransaction();
4 Organization org = (Organization) session.load(Organization.class, orgId);
5 org.addApplication(app);
6 assertTrue(org.getApplications().contains(app));
7
8 // Lets add a new App to the set
9 SmartApplication otherApp = new SmartApplication();
10 otherApp.setName("Foo Bar");
11 org.addApplication(otherApp);
12 assertEquals(2, org.getApplications().size());
13
14 session.update(org);
15 session.flush();
16
17 // Upon Saving if our collection's contains matches up, our code woiked!
18 assertTrue(otherApp.getId() != null);
19 assertTrue("More acid test", org.getApplications().contains(otherApp));
20 tx.commit();
21 }
A Maven example of the above is available HERE.  Enjoy!!!





No comments: