This post elaborates on an interesting Architecture bug (was made long time ago by unmet team) and the fix for it. To sum up it’s about entities purification out of the Persistence Context overhead when you need to use them outside of the container, serialize them and send them over RMI,SOAP and other protocols to other JVMs.

The Problem

You may found yourself in a situation where you don’t have DTOs (for whatever strange reason), there existing object-graph is large but you need to use entity objects outside of the managed scope. So what is a problem?

In the case of Hibernate first problem appears in a huge object footprint you’ll have to serialize by default even if you send just one detached entity. I found out that in some circumstances nearly the whole Hibernate Context is still connected to your detached entities. If you serialize such a huge but useless object graph every time, you slow down your client, last your network, create huge RMI/Web Service marshaling overhead, wastes client memory which even can provoke out of memory problems. All that you definitely don’t need in your application.

Different Solutions

So how to avoid this? - Sure rewrote everything. But sometimes it’s just not an option, so what else? We need a simple way to purify given entities at “low cost”. This means the solution has to be simple for developers, with minimal boilerplate code but also be perform at run-time. The output should contain clearly detached and as many as possible purified entities. It’s very important to cut any possible references to technical background objects of (hibernate) persistent context.

Such a cleaning makes only sense outside of the transaction scope. Then at the end of the transaction, all participated entities have to maintain their persistent state, so of course, they cannot be detached before that point. Otherwise detached and pruned entities cannot be persisted by the entity manager anymore and have to be refreshed first.

Do cleaning outside of transaction scope.

@TransactionAttribute(NEVER)
pubic MyEtity updateMyEntityMethod(MyEntity entity){
return SomePruner.prune(updateMyEntityMethodTransactional(entity));
}

@TransactionAttribute(REQUIRED)
protected MyEtity updateMyEntityMethodTransactional(MyEntity entity){
...
return entityManager.merge(entity);
}

You can use your updateMyEntityMethod() from outside, this is not transactional. The transaction only begins before updateMyEntityMethodTransactional() method is started and ends after return. The only question is how to minimize the entity footprint as usual several approaches may be used:

DIY: Clone what you need.

  • This is really fast if you do it well and flexible at all.
  • But blows your code up, and is hard to maintain.
  • Would you clone even objects you never wrote?

Clone selective by reflection

  • Can perform well enough at run-time
  • But you need reinvent the wheel, still need to maintain boilerplate code.

Serialize it selective by framework

  • Small footprint
  • Tends to be less performant
  • Can lead to problems with complex graphs (try to serialize bidirectional references to JSON).

Cloning (reflection based) libraries

  • Small footprint, easy usage
  • Very impressive performance
  • Can have restrictions on your design I just want to point out libraries of the last approach (of course you invited to discuss alternatives in the comments).

Purify entities by cloning

The cloning library from Kostantinos Kougios is very small, fast configurable and extendable reflection-based cloning library. So why not using it for our needs. It just not considered to be an entity pruner out of the box, but this is what I give you here. Every simple Cloner has to look similar:


com.rits.cloning.Cloner cloner=new Cloner();
MyClass clonedObject=cloner.deepClone(sourceObject);

This will perform a deep copy of everything, very fast. But what about the footprint you ask? You right we don’t need everything. Let’s tell the Cloner that we don’t want to have hibernated stuff in our clones. Here is an array of classes we don’t want to clone.

private final static Class[] SKIPCLASSES = new Class[] {
  SessionImplementor.class,
  JDBCTransaction.class,
  SessionImpl.class,
  StatelessSessionImpl.class,
  HibernateProxy.class,
JavassistLazyInitializer};

static {
  cloner = new Cloner();
  cloner.nullInsteadOfClone(SKIPCLASSES);
}

This will clone your entities without Hibernate context. You also encouraged to add additional user-defined classes to this list, if you don’t need them in your clones.

The next tip is to use fast cloners. I would use them for top-level entities because you normally don’t need all the stuff in the cloned entity. Leaving it at null increases speed (of course we are already at milliseconds). and keep the footprint pretty low.

 cloner.registerFastCloner(
 MyServerSideEntity.class, new MyServersideEnitityFastCloner());
 //Beware exact mach of class is used.

So even if cloner is pretty fast out of the box there are several approaches to adapt it to your needs or make it’s even faster. But in the context of JPA entities, we have to be more concerned about entity state than maybe about milliseconds of not high optimal cloning. Normally several paths on your object graph will be lazy initialized on the transaction end. Let’s assume when something is not initialized at the transaction end, that doesn’t need to be cloned as well. Let manifest this design decision in  the code:

cloner = new Cloner() {
@Override
protected Object fastClone(Object o, Map<Object, Object> clones)
                   throws IllegalAccessException {
   //If hibernate proxy collection.
   if (PersistentCollection.class.isAssignableFrom(o.getClass())) {
       if (((PersistentCollection) o).wasInitialized()) {
           return super.fastClone(o,clones());
        }else {
           return null; // or other routine for new empty Object.
        }
    }
   //If hibernate Entityproxy.
   if (HibernateProxy.class.isAssignableFrom(o.getClass())) {
       LazyInitializer initializer = ((HibernateProxy) o).getHibernateLazyInitializer();
        if (!initializer.isUninitialized()) {
             return super.fastClone(initializer.getImplementation(), clones);
        } else {
          return null;
        }
     }
     return super.fastClone(o, clones); //default approach.
   }
};

That’s it! Cool isn’t it?

Entity Pruner

The above solution utilizes general-purpose cloning library for Entity pruning, where entity-pruner seems to be a more elaborated solution directly for entity pruning and “unpruning” which even deserves to be considered in the initial architecture of the future applications. The adoption of Entity Pruner begins when your entities implement PrunableEntity interface which is used for the maintenance of entity prune state. Because of this restriction, I downgrade this solution in the context of my current problem. But if I would be able to implement such an interface, then I would get the same performance as by cloning the library out of the box.

import com.saliman.entitypruner.EntityPruner;
 ...
 @Stateless
 SomeFassadeSessionBean
 ...
 @EJB
 EntityPruner pruner;

 @TransactionAttribute(TransactionAttributeType.NEVER)
 public MyEntity getSomeEntity(){
  ...
 return pruner.prune((PrunableEntity)entity);
}

Above you see injected EntityPruner as an EJB. It works because EntityPruner library includes an implementation StatelesBean. It is e.g. hibernate aware implementation. The rest is like cloning library but in addition, there is unprune() method which allows the reintegration of received entities to the current persistent context. That makes entity pruner a complete solution and if you considering the usage of entity pruning in the next application maybe it’s a best time to look closer at entity pruner now.

DTO vs Entity Pruning

However, I can’t give you the definitive guide to the general question of whether DTOs are a must in every case or even DTOs are dead since EJB 3.0.

I still think it depends on a situation. But in general usage of DTOs introduces additional abstraction layer, clear and specific interfaces and therefore increases separation of concerns and flexibility. In opposite to this, you have to implement the transformation and maintain DTOs. Let discuss your experiences on this. Thank you if you read this!