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
EntityManagerper 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<EntityManager> entityManagerThreadLocal = new ThreadLocal<EntityManager>();
public static EntityManager getEntityManager() {
EntityManager entityManager = entityManagerThreadLocal.get();
if (entityManager == null) {
entityManager = emf.createEntityManager();
entityManagerThreadLocal.set(entityManager);
}
return entityManager;
}
public static <T> T getTransactionalProxy(Class<? 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);
}
}
}
On November 3rd, 2011,
posted in: JAVA (EE and SE) by Raphael Parree Tags: Hibernate, javase, javassist, JPA, proxy





