All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.ramerf.wind.spring.DaoUtils Maven / Gradle / Ivy

package io.github.ramerf.wind.spring;

import io.github.ramerf.wind.core.config.JdbcEnvironment;
import io.github.ramerf.wind.core.executor.Dao;
import io.github.ramerf.wind.core.executor.DaoFactory;
import io.github.ramerf.wind.core.util.Asserts;
import io.github.ramerf.wind.spring.transaction.SpringManagedTransactionFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.TransientDataAccessResourceException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;

@Slf4j
public final class DaoUtils {
  private static final String NO_DAO_FACTORY_SPECIFIED = "No DaoFactory specified";
  private static final String NO_DAO_SPECIFIED = "No Dao specified";

  /** This class can't be instantiated, exposes static utility methods only. */
  private DaoUtils() {
    // do nothing
  }

  public static Dao getDao(DaoFactory daoFactory) {
    return getDao(daoFactory, null);
  }

  public static Dao getDao(
      DaoFactory daoFactory, PersistenceExceptionTranslator exceptionTranslator) {
    Asserts.notNull(daoFactory, NO_DAO_FACTORY_SPECIFIED);
    DaoHolder holder = (DaoHolder) TransactionSynchronizationManager.getResource(daoFactory);
    Dao dao = daoHolder(holder);
    if (dao != null) {
      return dao;
    }

    log.debug("Creating a new Dao");
    dao = daoFactory.getDao();
    registerDaoHolder(daoFactory, exceptionTranslator, dao);
    return dao;
  }

  private static void registerDaoHolder(
      DaoFactory daoFactory, PersistenceExceptionTranslator exceptionTranslator, Dao dao) {
    DaoHolder holder;
    if (TransactionSynchronizationManager.isSynchronizationActive()) {
      JdbcEnvironment environment = daoFactory.getConfiguration().getJdbcEnvironment();
      if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
        log.debug("Registering transaction synchronization for Dao [" + dao + "]");

        holder = new DaoHolder(dao, exceptionTranslator);
        TransactionSynchronizationManager.bindResource(daoFactory, holder);
        TransactionSynchronizationManager.registerSynchronization(
            new DaoSynchronizationAdapter(holder, daoFactory));
        holder.setSynchronizedWithTransaction(true);
        holder.requested();
      } else {
        if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) {
          log.debug(
              "Dao ["
                  + dao
                  + "] was not registered for synchronization because DataSource is not transactional");
        } else {
          throw new TransientDataAccessResourceException(
              "DaoFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
        }
      }
    } else {
      log.debug(
          "Dao ["
              + dao
              + "] was not registered for synchronization because synchronization is not active");
    }
  }

  private static Dao daoHolder(DaoHolder holder) {
    Dao dao = null;
    if (holder != null && holder.isSynchronizedWithTransaction()) {
      holder.requested();
      log.debug("Fetched Dao [" + holder.getDao() + "] from current transaction");
      dao = holder.getDao();
    }
    return dao;
  }

  /**
   * Checks if {@code Dao} passed as an argument is managed by Spring {@code
   * TransactionSynchronizationManager} If it is not, it closes it, otherwise it just updates the
   * reference counter and lets Spring call the close callback when the managed transaction ends
   *
   * @param dao a target Dao
   * @param daoFactory a factory of Dao
   */
  public static void closeDao(Dao dao, DaoFactory daoFactory) {
    Asserts.notNull(dao, NO_DAO_SPECIFIED);
    Asserts.notNull(daoFactory, NO_DAO_FACTORY_SPECIFIED);

    DaoHolder holder = (DaoHolder) TransactionSynchronizationManager.getResource(daoFactory);
    if ((holder != null) && (holder.getDao() == dao)) {
      log.debug("Releasing transactional Dao [" + dao + "]");
      holder.released();
    } else {
      log.debug("Closing non transactional Dao [" + dao + "]");
      dao.close();
    }
  }

  public static boolean isDaoTransactional(Dao dao, DaoFactory daoFactory) {
    Asserts.notNull(dao, NO_DAO_SPECIFIED);
    Asserts.notNull(daoFactory, NO_DAO_FACTORY_SPECIFIED);
    DaoHolder holder = (DaoHolder) TransactionSynchronizationManager.getResource(daoFactory);
    return (holder != null) && (holder.getDao() == dao);
  }

  /**
   * Callback for cleaning up resources. It cleans TransactionSynchronizationManager and also
   * commits and closes the {@code Dao}. It assumes that {@code Connection} life cycle will be
   * managed by {@code DataSourceTransactionManager} or {@code JtaTransactionManager}
   */
  private static final class DaoSynchronizationAdapter extends TransactionSynchronizationAdapter {
    private final DaoHolder holder;
    private final DaoFactory daoFactory;
    private boolean holderActive = true;

    public DaoSynchronizationAdapter(DaoHolder holder, DaoFactory daoFactory) {
      Asserts.notNull(holder, "Parameter 'holder' must be not null");
      Asserts.notNull(daoFactory, "Parameter 'daoFactory' must be not null");

      this.holder = holder;
      this.daoFactory = daoFactory;
    }

    /** {@inheritDoc} */
    @Override
    public int getOrder() {
      // order right before any Connection synchronization
      return DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 1;
    }

    /** {@inheritDoc} */
    @Override
    public void suspend() {
      if (this.holderActive) {
        log.debug("Transaction synchronization suspending Dao [" + this.holder.getDao() + "]");
        TransactionSynchronizationManager.unbindResource(this.daoFactory);
      }
    }

    /** {@inheritDoc} */
    @Override
    public void resume() {
      if (this.holderActive) {
        log.debug("Transaction synchronization resuming Dao [" + this.holder.getDao() + "]");
        TransactionSynchronizationManager.bindResource(this.daoFactory, this.holder);
      }
    }

    /** {@inheritDoc} */
    @Override
    public void beforeCommit(boolean readOnly) {
      if (TransactionSynchronizationManager.isActualTransactionActive()) {
        try {
          log.debug("Transaction synchronization committing Dao [" + this.holder.getDao() + "]");
          this.holder.getDao().commit();
        } catch (WindUncategorizedDataAccessException p) {
          if (this.holder.getPersistenceExceptionTranslator() != null) {
            DataAccessException translated =
                this.holder.getPersistenceExceptionTranslator().translateExceptionIfPossible(p);
            if (translated != null) {
              throw translated;
            }
          }
          throw p;
        }
      }
    }

    /** {@inheritDoc} */
    @Override
    public void beforeCompletion() {
      if (!this.holder.isOpen()) {
        log.debug("Transaction synchronization deregistering Dao [" + this.holder.getDao() + "]");
        TransactionSynchronizationManager.unbindResource(daoFactory);
        this.holderActive = false;
        log.debug("Transaction synchronization closing Dao [" + this.holder.getDao() + "]");
        this.holder.getDao().close();
      }
    }

    /** {@inheritDoc} */
    @Override
    public void afterCompletion(int status) {
      if (this.holderActive) {
        log.debug("Transaction synchronization deregistering Dao [" + this.holder.getDao() + "]");
        TransactionSynchronizationManager.unbindResourceIfPossible(daoFactory);
        this.holderActive = false;
        log.debug("Transaction synchronization closing Dao [" + this.holder.getDao() + "]");
        this.holder.getDao().close();
      }
      this.holder.reset();
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy