In my post Java/Hibernate: Converting Hibernate proxy to real object and ORMLazyLoader, we have studied an utility ORMLazyLoader used directly in the POJO in order to detach from Hibernate session the lazy loaded attributes like:

public class Chapter extends AbstractPersistentObject {	
...	
	public Book getBook() {
		book = ORMUtils.initializeAndUnproxy(book);
		
		return book;
	}
...
}

This utility loads the lazy attributes (Hibernate, Collection) in specified levels, however, the attributes of levels not specified, will remain attached to the Hibernate session. So, if in the presentation layer, we try to access to an attribute attached to Hibernate Session, we will have the famous LazyInitializationException.

ORMCloner vs ORMLazyLoader
So, to avoid totally Hibernate LazyInitializationException in presentation layer, like the previous lazy loaded ORMLazyLoader solution, this utility ORMCloner resolves recursively the proxies and clones the POJO with lazy attributes attached to Hibernate Session.

More, the attributes which are out of the cloning’s scope are null because they are not attached to session and their detachment is not asked.

So, the returned and cloned POJO is totally detached from persistence Session, however, it will be necessary to load it again in order to use it in the persistence layer.

This solution is based on the HashCode, Equals methods of persistence POJO (extending AbstractPersistentObject) and uses the commons librairies commons-beanutils-1.7.0.jar and commons-lang-2.4.jar in particular to copy attributes from one object attached to an identical cloned/ghost object.

Uses of ORMCloner
Simple example:

String[] levelsToCopy = new String[] {"language", "chapters/book", "authors"};
// This bean is attached to Hibernate Session
Book hibernateAttachedBean = new Book();
...
Book hibernateDetachedBean = (new ORMCloner<Book>()).clone(hibernateAttachedBean, levelsToCopy);

Other concrete examples:

  • load all primitive attributes of:
    – “language” attribute of first level,
    – each instance “chapter” in “chapters” attribute of first level,
    – each instance “book” of second level in above “chapters/chapter” attribute of first level,
    – each instance “author” in “authors” of first level.

    new ORMCloner<Book>()).clone(bean, new String{"language", "chapters/book", "authors"});
    
  • load all primitive attributes of the first level:
    (new ORMCloner<Book>()).clone(bean, new String[] {});
    
  • load all attributes (primitive, collection, hibernate types) of the first level:
    new ORMCloner<Book>()).clone(bean, new String[] {"*"}); 
    
  • load all attributes (primitive, collection, hibernate types) of the first and second levels:
    (new ORMCloner<Book>()).clone(bean, new String{"*&#47*"});
    

The source code of this class ORMCloner and a test case containing examples ORMClonerTest are in the ZIP file attachement. This project needs the following librairies commons-beanutils-1.7.0.jar, commons-lang-2.4.jar, commons-logging-1.1.1.jar, hibernate-core-3.3.1.GA.jar, junit-4.1.jar and spring-2.5.5.jar.

Other solution: Open Session In View Pattern
An other solution consists into to use the Open Session In View pattern. In a web application, this pattern may be implemented for example using a servlet/http filter. The arrival of an HTTP request initiates the opening of a transaction and the corresponding Hibernate session. Once, the view made ​​and ready to be returned to the client, the session is closed and the transaction is committed. The Open Session In View pattern can not be applied in a 3-tier technical architecture where the presentation layer and business layer are physically deployed on two different servers, and therefore two different JVMs. This pattern is also not valid in the context of a rich web application using Ajax and JavaScript to retrieve and browse through the business model.

Download: test_ORMTools.zip

Best regards,

Huseyin OZVEREN