package io.dropwizard.hibernate;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.context.internal.ManagedSessionContext;

import javax.annotation.Nullable;
import java.util.Map;

import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.Objects.requireNonNull;

An aspect providing operations around a method with the UnitOfWork annotation. It opens a Hibernate session and optionally a transaction.

It should be created for every invocation of the method.

Usage :


  UnitOfWorkProxyFactory unitOfWorkProxyFactory = ...
  UnitOfWork unitOfWork = ...         // get annotation from method.
  UnitOfWorkAspect aspect = unitOfWorkProxyFactory.newAspect();
  try {
    aspect.beforeStart(unitOfWork);
    ...                               // perform business logic.
    aspect.afterEnd();
  } catch (Exception e) {
    aspect.onError();
    throw e;
  } finally {
    aspect.onFinish();
  }
 
/** * An aspect providing operations around a method with the {@link UnitOfWork} annotation. * It opens a Hibernate session and optionally a transaction. * <p>It should be created for every invocation of the method.</p> * <p>Usage :</p> * <pre> * {@code * UnitOfWorkProxyFactory unitOfWorkProxyFactory = ... * UnitOfWork unitOfWork = ... // get annotation from method. * * UnitOfWorkAspect aspect = unitOfWorkProxyFactory.newAspect(); * try { * aspect.beforeStart(unitOfWork); * ... // perform business logic. * aspect.afterEnd(); * } catch (Exception e) { * aspect.onError(); * throw e; * } finally { * aspect.onFinish(); * } * } * </pre> */
public class UnitOfWorkAspect { private final Map<String, SessionFactory> sessionFactories; public UnitOfWorkAspect(Map<String, SessionFactory> sessionFactories) { this.sessionFactories = sessionFactories; } // Context variables @Nullable private UnitOfWork unitOfWork; @Nullable private Session session; @Nullable private SessionFactory sessionFactory; public void beforeStart(@Nullable UnitOfWork unitOfWork) { if (unitOfWork == null) { return; } this.unitOfWork = unitOfWork; sessionFactory = sessionFactories.get(unitOfWork.value()); if (sessionFactory == null) { // If the user didn't specify the name of a session factory, // and we have only one registered, we can assume that it's the right one. if (unitOfWork.value().equals(HibernateBundle.DEFAULT_NAME) && sessionFactories.size() == 1) { sessionFactory = sessionFactories.values().iterator().next(); } else { throw new IllegalArgumentException("Unregistered Hibernate bundle: '" + unitOfWork.value() + "'"); } } session = sessionFactory.openSession(); try { configureSession(); ManagedSessionContext.bind(session); beginTransaction(unitOfWork, session); } catch (Throwable th) { session.close(); session = null; ManagedSessionContext.unbind(sessionFactory); throw th; } } public void afterEnd() { if (unitOfWork == null || session == null) { return; } try { commitTransaction(unitOfWork, session); } catch (Exception e) { rollbackTransaction(unitOfWork, session); throw e; } // We should not close the session to let the lazy loading work during serializing a response to the client. // If the response successfully serialized, then the session will be closed by the `onFinish` method } public void onError() { if (unitOfWork == null || session == null) { return; } try { rollbackTransaction(unitOfWork, session); } finally { onFinish(); } } public void onFinish() { try { if (session != null) { session.close(); } } finally { session = null; ManagedSessionContext.unbind(sessionFactory); } } protected void configureSession() { checkNotNull(unitOfWork); checkNotNull(session); session.setDefaultReadOnly(unitOfWork.readOnly()); session.setCacheMode(unitOfWork.cacheMode()); session.setHibernateFlushMode(unitOfWork.flushMode()); } private void beginTransaction(UnitOfWork unitOfWork, Session session) { if (!unitOfWork.transactional()) { return; } session.beginTransaction(); } private void rollbackTransaction(UnitOfWork unitOfWork, Session session) { if (!unitOfWork.transactional()) { return; } final Transaction txn = session.getTransaction(); if (txn != null && txn.getStatus().canRollback()) { txn.rollback(); } } private void commitTransaction(UnitOfWork unitOfWork, Session session) { if (!unitOfWork.transactional()) { return; } final Transaction txn = session.getTransaction(); if (txn != null && txn.getStatus().canRollback()) { txn.commit(); } } protected Session getSession() { return requireNonNull(session); } protected SessionFactory getSessionFactory() { return requireNonNull(sessionFactory); } }