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: , ,