Archive

Archive for March, 2009

Hibernate Deep Deproxy

March 16th, 2009 2 comments

A common problem faced with using ORMs that use lazy loading is that the objects returned by the ORM contain (obviously) lazy loading references, so that you need an ORM session to access those objects. For example, if you have a “Person” class, that contains a “mother” property, when you do “person.getMother()”, the ORM will get the mother from the database when it’s requested – not when the person is initialized.

Lazy loading is great, because it means you don’t load a huge amount of data when you really just want one object (say you just want the person’s name, with lazy loading, the person’s mother is never retrieved). However, when you want to do caching, lazy loading can be a serious problem.

For example, let’s say I have a method I call a lot – “personDao.findAll()”. I’d like to cache this entire method, so I don’t need to hit the database or the ORM at all, so I use something like an aspect to do declarative caching on that method. On the second and subsequent calls, the returned list of persons won’t have sessions attached (as they’re still attached to the first caller, which is long gone), so they can’t load their lazy references, and you end up with the famous LazyInitializationException. If you know the list of people isn’t too big, and that it doesn’t refer to too many other objects, you can removed the lazy proxies and load everything at once – then cache that result. But be careful – by doing deep deproxying, all objects that are refered to will be loaded, so if you’re not careful, you can load the entire database, which results is either a loss of performance (due to using all the memory) or an immediate error.

Here’s how I do deep deproxying with Hibernate. I’ve read about many techniques to do this, but this approach works for better than anything I’ve been able to find so far.

    public T deepDeproxy(final Object maybeProxy) throws ClassCastException {
    	if(maybeProxy==null) return null;
		T ret = deepDeproxy(maybeProxy,new HashSet<Object>());
    	return ret;
    }
 
    private T deepDeproxy(final Object maybeProxy,final HashSet<Object> visited) throws ClassCastException {
    	if(maybeProxy==null) return null;
        Class clazz;
        Hibernate.initialize(maybeProxy);
        if (maybeProxy instanceof HibernateProxy) {
                HibernateProxy proxy = (HibernateProxy) maybeProxy;
                LazyInitializer li = proxy.getHibernateLazyInitializer();
                clazz = li.getImplementation().getClass();
        }
        else {
                clazz = maybeProxy.getClass();
        }
        T ret = (T) deepDeproxy(maybeProxy,clazz);
        if(visited.contains(ret)) return ret;
        visited.add(ret);
        for (PropertyDescriptor property : PropertyUtils.getPropertyDescriptors(ret)) {
        	try{
	        	String name = property.getName();
        		if(!"owner".equals(name) &&  property.getWriteMethod()!=null){
        			Object value = PropertyUtils.getProperty(ret, name);
    				boolean needToSetProperty=false;
        			if (value instanceof HibernateProxy) {
    					value = deepDeproxy(value,visited);
    					needToSetProperty=true;
        			}
        			if(value instanceof Object[]){
        				Object[] valueArray = (Object[]) value;
        				Object[] result = (Object[]) Array.newInstance(value.getClass(), valueArray.length);
        				for(int i=0;i<valueArray.length;i++){
        					result[i]=deepDeproxy(valueArray[i],visited);
        				}
        				value=result;
        				needToSetProperty=true;
        			}
        			if(value instanceof Set){
        				Set valueSet = (Set) value;
        				Set result = new HashSet();
        				for(Object o : valueSet){
        					result.add(deepDeproxy(o,visited));
        				}
        				value=result;
    					needToSetProperty=true;
        			}
        			if(value instanceof Map){
        				Map valueMap = (Map) value;
        				Map result = new HashMap();
        				for(Object o : valueMap.keySet()){
        					result.put(deepDeproxy(o, visited),deepDeproxy(valueMap.get(o),visited));
        				}
        				value=result;
    					needToSetProperty=true;
        			}
        			if(value instanceof List){
        				List valueList = (List) value;
        				List result = new ArrayList(valueList.size());
        				for(Object o : valueList){
        					result.add(deepDeproxy(o,visited));
        				}
        				value=result;
    					needToSetProperty=true;
        			}
					if(needToSetProperty) PropertyUtils.setProperty(ret, name, value);
        		}
        	}catch (java.lang.IllegalAccessException e){
				e.printStackTrace();
        	} catch (InvocationTargetException e) {
				e.printStackTrace();
			} catch (NoSuchMethodException e) {
				e.printStackTrace();
			}
        }
        return ret;
    }
 
    private <T> T deepDeproxy(Object maybeProxy, Class<T> baseClass) throws ClassCastException {
    	if(maybeProxy==null) return null;
        if (maybeProxy instanceof HibernateProxy){
        	return baseClass.cast(((HibernateProxy) maybeProxy).getHibernateLazyInitializer().getImplementation());
        }else{
           return baseClass.cast(maybeProxy);
        }
     }
Categories: Uncategorized Tags:

EhCache implementation of OpenJPA caching

March 12th, 2009 2 comments

I usually use Hibernate, which supports a number of caching implementations (such as EhCache, oscache, JBoss, etc). My most recent project had a dependency on a product which has a dependency on OpenJPA, and OpenJPA only has it’s own built in implementations of a query cache and a data cache. I like to have one caching implementation in my project, so having two (OpenJPA’s for itself, and EhCache for everything else) annoyed me. So I had to fix it.

I started with Pinaki Poddar’s implementation of a Coherence provided OpenJPA data cache. I changed it to use EhCache, adjusted the unit tests, and then added a query cache implementation. To use it, add a dependency on the openjpa-ehcache, then set OpenJPA’s “openjpa.QueryCache” to “ehcache” and “openjpa.DataCacheManager” to ehcache. That’s it!

The code can be compiled with Maven. Simply run “mvn install”.

My code, like EhCache and OpenJPA, is licensed under the Apache Public License 2.0. Get it here.

Categories: Uncategorized Tags: