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

[sourcecode language="java"]
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); } [/sourcecode]

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

[sourcecode language="java"] 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);

}

}

}
[/sourcecode]

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