Wednesday, March 9, 2011

Delazifying Hibernate Entitities.

Not everything Hibernate promises you, when it handles you an object, and that may be virtually the entire object graph for your application, is available immediately. Sometimes what you got is not the object itself but just a promise to deliver it, when needed. A.k.a a proxy. And as long as the Hibernate session is not closed and available, at the place/scope where objects are used the promises are meet and the values are delivered.

Still, sometimes, the persisted and then retrieved objects leave the scope where the Hibernate sessions live. And some carry with them the "unrealized" proxies. And if such are objects handled over to some other framework, blissfully unaware of Hibernate proxies, then there would be some clash of opinions and throwing of exceptions. Take BlazeDS...

So?

public Object stripLazyProxies(Object object) {
  if (object instanceof Collection<?>) {
   Collection<?> list = (Collection<?>) object;
   for (Object inner : list) {
    stripLazyProxies(inner);
   }
  } else {
   Class<? extends Object> cls = object.getClass();
   Field[] fields = cls.getDeclaredFields();
   for (Field field : fields) {
    // Skip static fields
    if (Modifier.isStatic(field.getModifiers())) {
     continue;
    }
    OneToMany oneToMany = field.getAnnotation(OneToMany.class);
    if (oneToMany != null && oneToMany.fetch() == FetchType.LAZY) {
     field.setAccessible(true);
     try {
      field.set(object, null);
     } catch (Exception e) {
      throw new RuntimeException(e);
     }
     continue;
    }
    ManyToOne manyToOne = field.getAnnotation(ManyToOne.class);
    if (manyToOne != null && manyToOne.fetch() == FetchType.LAZY) {
     field.setAccessible(true);
     try {
      field.set(object, null);
     } catch (Exception e) {
      throw new RuntimeException(e);
     }
     continue;
    }
    ManyToMany manyToMany = field.getAnnotation(ManyToMany.class);
    if (manyToMany != null && manyToMany.fetch() == FetchType.LAZY) {
     field.setAccessible(true);
     try {
      field.set(object, null);
     } catch (Exception e) {
      throw new RuntimeException(e);
     }
     continue;
    }
    OneToOne oneToOne = field.getAnnotation(OneToOne.class);
    if (oneToOne != null && oneToOne.fetch() == FetchType.LAZY) {
     field.setAccessible(true);
     try {
      field.set(object, null);
     } catch (Exception e) {
      throw new RuntimeException(e);
     }
     continue;
    }

    // Any other annotations with FetchType.LAZY ??

    // Check for inner entities
    Entity entity = field.getType().getAnnotation(Entity.class);
    if (entity != null) {
     field.setAccessible(true);
     try {
      Object value = field.get(object);
      if (value != null) {
       field.set(object, stripLazyProxies(value));
      }
     } catch (Exception e) {
      throw new RuntimeException(e);
     }
    }
   }
  }
  return object;
 }

The method above will set recursively set every "lazy" field to null. I used it, in order to avoid doing the same by hand. If setting every field to null is too much for you, then obviously you need some other method.

No comments:

Post a Comment