*/
package org.wamblee.support.persistence;
+
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* This utility makes sure that each invocation on a certain interface is
- * carried out within a JPA unit of work.
+ * carried out within a JPA unit of work. Note that this is equivalent
+ * to the sementics of a requiresNew transaction attribute.
+ *
+ * Use {@link #getTransactionScopedEntityManager()} to get the transaction
+ * scoped entity manager to pass to services.
*
- * Use {@link #getTransactionScopedEntityManager()} to get the transaction scoped
- * entity manager to pass to services.
+ *
+ * For example:
+ * <pre>
+ * JpaBuilder builder = ...
+ * TransactionProxyFactory<Service> factory = new TransactionProxyFactory<Service>(
+ * builder, Service.class);
+ * Service service = new JpaService(factory.getTransactionScopedEntityManager());
+ * Service proxy = factory.getProxy(service);
+ * proxy.executeMethod(...);
+ * </pre>
+ * The above example executes the executeMethod() call on the service object within an active transaction.
+ * In the constructor of the service a transaction scoped entity manager is passed.
*
* @param T
* Type of interface to proxy.
*/
public class TransactionProxyFactory<T> {
+ /**
+ * Executes the call on the service within a new transaction.
+ *
+ * @author Erik Brakkee
+ *
+ * @param <T> Type of the service interface.
+ */
private class UnitOfWorkInvocationHandler<T> implements InvocationHandler {
private T service;
.execute(new JpaUnitOfWork<Object>() {
@Override
public Object execute(EntityManager aEm) throws Exception {
+ EntityManager oldEm = ENTITY_MANAGER.get();
try {
ENTITY_MANAGER.set(aEm);
return aMethod.invoke(service, aArgs);
} catch (InvocationTargetException e) {
- throw (Exception)e.getCause();
- } finally {
- ENTITY_MANAGER.set(null);
+ Throwable cause = e.getCause();
+ if (cause instanceof Exception) {
+ throw (Exception) cause;
+ } else if (cause instanceof Error) {
+ throw (Error) cause;
+ }
+ // last resort.
+ throw new RuntimeException(e);
+ } finally {
+ ENTITY_MANAGER.set(oldEm);
}
}
});
}
- private static final ThreadSpecificProxyFactory<EntityManager> ENTITY_MANAGER =
- new ThreadSpecificProxyFactory<EntityManager>(EntityManager.class);
-
+ private static final ThreadSpecificProxyFactory<EntityManager> ENTITY_MANAGER = new ThreadSpecificProxyFactory<EntityManager>(
+ EntityManager.class);
+
private JpaBuilder jpaBuilder;
private Class<T> clazz;
jpaBuilder = aJpaBuilder;
clazz = aClass;
}
-
- public EntityManager getTransactionScopedEntityManager() {
+
+ public EntityManager getTransactionScopedEntityManager() {
return ENTITY_MANAGER.getProxy();
}
new Class[] { InvocationHandler.class }).newInstance(
new Object[] { handler });
return proxy;
- } catch (Exception e) {
- throw new RuntimeException("Could not create proxy for " + clazz.getName(), e);
+ } catch (Exception e) {
+ throw new RuntimeException("Could not create proxy for " +
+ clazz.getName(), e);
}
}
}