Skip to content

Instantly share code, notes, and snippets.

@mkurz
Last active April 12, 2022 12:29
Show Gist options
  • Save mkurz/7108004cc9509b809b6280e81bbdf88c to your computer and use it in GitHub Desktop.
Save mkurz/7108004cc9509b809b6280e81bbdf88c to your computer and use it in GitHub Desktop.
Reuse the same entity manager for each request
package foo;
import java.lang.reflect.Method;
import java.util.concurrent.CompletionStage;
import javax.inject.Inject;
import play.Logger;
import play.db.jpa.JPAApi;
import play.mvc.Action;
import play.mvc.Http;
import play.mvc.Result;
/**
* ActionCreator which wraps each action into a transaction and tries to commit (or rollback if
* desired) and close the transaction and entity manager.
*/
public class ActionCreator implements play.http.ActionCreator {
private static final Logger.ALogger log = Logger.of(ActionCreator.class);
private final JPAApi jpaApi;
@Inject
public ActionCreator(final JPAApi jpaApi) {
this.jpaApi = jpaApi;
}
@Override
public Action<?> createAction(final Http.Request request, final Method actionMethod) {
return new Action.Simple() {
@Override
public CompletionStage<Result> call(Http.Request req) {
if (log.isDebugEnabled()) {
log.debug(
"JPAActionCreator withTransaction; Method: {}, existing em from req attr: {}",
logActionMethod(),
req.attrs().getOptional(ReqAttrsJPA.APP_EM).orElse(null));
}
final var em = ActionCreator.this.jpaApi.em("default");
em.getTransaction().begin();
return this.delegate
.call(req.addAttr(ReqAttrsJPA.APP_EM, em))
.whenComplete(
(result, error) -> {
final var doRollback = error != null;
final var activeTransaction =
em.getTransaction() != null && em.getTransaction().isActive();
if (log.isDebugEnabled()) {
log.debug(
"JPAActionCreator whenComplete; Method: {}, em: {}, doRollback = {}, active transaction? {}, getRollbackOnly already set? {}",
logActionMethod(),
em,
doRollback,
activeTransaction,
em.getTransaction() != null
? em.getTransaction().getRollbackOnly()
: "<no active transaction>");
}
if (doRollback && activeTransaction) {
em.getTransaction().setRollbackOnly();
}
JPAUtils.closeTxAndEm(em);
});
}
private String logActionMethod() {
return actionMethod != null ? actionMethod.getName() : "null (maybe ErrorHandler?)";
}
};
}
}
# You have to add this to your application.conf
# See
# https://www.playframework.com/documentation/2.8.x/JavaActionCreator
play.http.actionCreator = foo.ActionCreator
package foo;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import org.hibernate.Filter;
import org.hibernate.Session;
import org.hibernate.internal.FilterImpl;
import org.hibernate.internal.SessionImpl;
import play.Logger;
public class JPAUtils {
private static final Logger.ALogger log = Logger.of(JPAUtils.class);
public static void closeTxAndEm(final EntityManager em) {
final EntityTransaction tx = em.getTransaction();
if (tx != null && tx.isActive()) {
if (tx.getRollbackOnly()) {
log.debug(
"ROLLBACK transaction: EM: {} - Transaction: {}...",
() -> em,
() -> em.getTransaction());
tx.rollback();
log.debug(
"...ROLLBACK transaction FINISHED: EM: {} - Transaction: {}",
() -> em,
() -> em.getTransaction());
} else {
log.debug(
"COMMIT transaction: EM: {} - Transaction: {}...", () -> em, () -> em.getTransaction());
tx.commit();
log.debug(
"...COMMIT transaction FINISHED: EM: {} - Transaction: {}",
() -> em,
() -> em.getTransaction());
}
}
if (em.isOpen()) {
log.debug("CLOSING EM: {}...", em);
em.close();
log.debug("CLOSING EM FINISHED: {}", em);
}
}
}
package foo;
import play.libs.typedmap.TypedKey;
import javax.persistence.EntityManager;
import java.util.List;
public class ReqAttrsJPA {
public static final TypedKey<EntityManager> APP_EM = TypedKey.create("jpa-app-entity-manager");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment