JPA Transactional Proxy for JavaSE

When using JPA in Java SE, it would still be helpful if a proxy would take care of the transactions and EntityManager life-cycle. I have created such a proxy for my Java/JPA and thought that perhaps others would like to use it as well.

Here is some client code using it

Foo f = JPAUtil.getTransactionalProxy(Foo.class);
f.bar();
</pre>

<p>The <code>bar</code> method can then be implemented like this</p>
<pre lang="java" escaped="true">
public void bar(){
  EntityManager em = JPAUtil.getEntityManager();
  ...
  em.persist(someObject);
}

The JPAUtil

is used to create the proxy and to obtain the EntityManager. The proxy is created using javassist

It creates an JPA transactional proxy with following semantics:

  • Shares an EntityManager per thread
  • Opens and closes the entityManager for each method call
  • Transaction follow REQUIRED pattern (will join an existing transaction)
  • Uses the rollback only flag on the Transaction object
  • Rolls-back on «any» exception

This implementation does not inject the EntityManager into the target object

public class JPAUtil {
  private static Logger logger = LoggerFactory.getLogger(JPAUtil.class);
  static final EntityManagerFactory emf;

  static {
    emf = Persistence.createEntityManagerFactory("manager1");
  }


  private static ThreadLocal&lt;EntityManager> entityManagerThreadLocal = new ThreadLocal&lt;EntityManager>();

  public static EntityManager getEntityManager() {
    EntityManager entityManager = entityManagerThreadLocal.get();
    if (entityManager == null) {
      entityManager = emf.createEntityManager();
      entityManagerThreadLocal.set(entityManager);
    }
    return entityManager;

  }

  public static &lt;T> T getTransactionalProxy(Class&lt;? extends T> clazz) {
    ProxyFactory f = new ProxyFactory();
    f.setSuperclass(clazz);
    f.setFilter(new MethodFilter() {

      public boolean isHandled(Method m) {
        return Modifier.isPublic(m.getModifiers());
      }
    });
    Class c = f.createClass();
    MethodHandler mi = new MethodHandler() {


         public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
                final EntityManager entityManager = getEntityManager();
                boolean needToClose = true;
                try {
                    final EntityTransaction transaction = entityManager.getTransaction();
                    boolean iOwnTransaction = !transaction.isActive();
                    needToClose = iOwnTransaction;
                    if (iOwnTransaction) {
                        transaction.begin();
                    }
                    Object returnValue = null;
                    try {
                        returnValue = proceed.invoke(self, args);
                        if (iOwnTransaction) {
                            if (transaction.getRollbackOnly()) {
                                logger.info("Rolling back transaction, as flag has been set");
                                transaction.rollback();
                            } else
                                transaction.commit();
                        }
                    } catch (InvocationTargetException e) {

                        if (iOwnTransaction) {
                            logger.info("Rolled back transaction due to exception", e);
                            transaction.rollback();
                        } else {
                            logger.info("Setting rollback-only flag due to exception", e);
                            transaction.setRollbackOnly();
                        }
                        throw e.getTargetException();
                    }
                    return returnValue;
                } finally {

                    if (needToClose && entityManager.isOpen()) {
                        entityManager.close();
                        entityManagerThreadLocal.remove();
                    }
                }


            }

    };
    final T instance;
    try {
      instance = (T) c.newInstance();
      ((ProxyObject) instance).setHandler(mi);
      return instance;
    } catch (InstantiationException e) {
      throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
      throw new RuntimeException(e);

    }

  }

}
Be Sociable, Share!
On November 3rd, 2011, posted in: JAVA (EE and SE) by Tags: , , , ,