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

org.jfaster.mango.transaction.TransactionImpl Maven / Gradle / Ivy

/*
 * Copyright 2014 mango.jfaster.org
 *
 * The Mango Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

package org.jfaster.mango.transaction;

import org.jfaster.mango.transaction.exception.IllegalTransactionStateException;
import org.jfaster.mango.transaction.exception.TransactionSystemException;
import org.jfaster.mango.util.logging.InternalLogger;
import org.jfaster.mango.util.logging.InternalLoggerFactory;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * @author ash
 */
public class TransactionImpl implements Transaction {

  private final static InternalLogger logger = InternalLoggerFactory.getInstance(TransactionImpl.class);

  private final boolean newTransaction;

  private final DataSource dataSource;

  private final Integer previousLevel;

  private final boolean mustRestoreAutoCommit;

  private boolean completed = false;

  private boolean rollbackOnly = false;

  public TransactionImpl(boolean newTransaction, DataSource dataSource) {
    this(newTransaction, dataSource, null, true);
  }

  public TransactionImpl(boolean newTransaction, DataSource dataSource,
                         Integer previousLevel, boolean mustRestoreAutoCommit) {
    this.newTransaction = newTransaction;
    this.dataSource = dataSource;
    this.previousLevel = previousLevel;
    this.mustRestoreAutoCommit = mustRestoreAutoCommit;
  }

  @Override
  public void commit() {
    if (completed) {
      throw new IllegalTransactionStateException(
          "Transaction is already completed - do not call commit or rollback more than once per transaction");
    }

    ConnectionHolder connHolder = TransactionSynchronizationManager.getConnectionHolder(dataSource);
    if (connHolder == null) {
      throw new IllegalStateException("No ConnectionHolder bind to DataSource [" + dataSource + "]");
    }

    if (!newTransaction) { // 嵌套的事务,不真正提交
      if (logger.isDebugEnabled()) {
        logger.debug("Commit transaction is not new");
      }
      if (rollbackOnly) {
        if (logger.isDebugEnabled()) {
          logger.debug("Marking transaction as rollback-only");
        }
        connHolder.setRollbackOnly(true); // 促发顶层事务回滚
      }
      return;
    }

    if (rollbackOnly || connHolder.isRollbackOnly()) { // 嵌套的事务出现回滚则回滚
      if (logger.isDebugEnabled()) {
        logger.debug("Transaction is marked as rollback-only, so will rollback");
      }
      processRollback(connHolder.getConnection());
      return;
    }

    // 提交事务
    processCommit(connHolder.getConnection());
  }

  @Override
  public void rollback() {
    if (completed) {
      throw new IllegalTransactionStateException(
          "Transaction is already completed - do not call commit or rollback more than once per transaction");
    }

    ConnectionHolder connHolder = TransactionSynchronizationManager.getConnectionHolder(dataSource);
    if (connHolder == null) {
      throw new IllegalStateException("No ConnectionHolder bind to DataSource [" + dataSource + "]");
    }

    if (!newTransaction) {
      if (logger.isDebugEnabled()) {
        logger.debug("Rollback transaction is not new, marking transaction as rollback-only");
      }
      connHolder.setRollbackOnly(true); // 促发顶层事务回滚
      return;
    }

    processRollback(connHolder.getConnection());
  }

  @Override
  public boolean isRollbackOnly() {
    return rollbackOnly;
  }

  @Override
  public void setRollbackOnly(boolean rollbackOnly) {
    this.rollbackOnly = rollbackOnly;
  }

  private void processCommit(Connection conn) {
    try {
      doCommit(conn);
    } catch (Exception e) {
      doRollbackOnCommitException(conn, e);
    } finally {
      cleanup(conn);
    }
  }

  private void doCommit(Connection conn) {
    try {
      if (logger.isDebugEnabled()) {
        logger.debug("Committing JDBC transaction on Connection [" + conn + "]");
      }
      conn.commit();
    } catch (SQLException e) {
      throw new TransactionSystemException("Could not commit JDBC transaction", e);
    }
  }

  private void doRollbackOnCommitException(Connection conn, Exception e) {
    try {
      doRollback(conn);
    } catch (TransactionSystemException tes) {
      logger.error("Commit exception overridden by rollback exception", e);
      throw tes;
    }
  }

  private void processRollback(Connection conn) {
    try {
      doRollback(conn);
    } finally {
      cleanup(conn);
    }
  }

  private void doRollback(Connection conn) {
    try {
      if (logger.isDebugEnabled()) {
        logger.debug("Rolling back JDBC transaction on Connection [" + conn + "]");
      }
      conn.rollback();
    } catch (SQLException e) {
      throw new TransactionSystemException("Could not roll back JDBC transaction", e);
    }
  }

  private void cleanup(Connection conn) {
    TransactionSynchronizationManager.unbindConnectionHolder(dataSource);
    resetConnectionAfterTransaction(conn);
    DataSourceUtils.releaseConnection(conn, dataSource);
    completed = true;
  }

  private void resetConnectionAfterTransaction(Connection conn) {

    try {
      if (mustRestoreAutoCommit) {
        if (logger.isDebugEnabled()) {
          logger.debug("Switching JDBC Connection to auto commit");
        }
        conn.setAutoCommit(true);
      }
    } catch (SQLException e) {
      DataSourceMonitor.resetAutoCommitFail(dataSource);
      logger.error("Could not reset autoCommit of JDBC Connection after transaction", e);
    } catch (Throwable e) {
      DataSourceMonitor.resetAutoCommitFail(dataSource);
      logger.error("Unexpected exception on resetting autoCommit of JDBC Connection after transaction", e);
    }
    try {
      if (previousLevel != null) {
        if (logger.isDebugEnabled()) {
          logger.debug("Resetting isolation level of JDBC Connection to " + previousLevel);
        }
        conn.setTransactionIsolation(previousLevel);
      }
    } catch (SQLException e) {
      logger.error("Could not reset isolation level of JDBC Connection after transaction", e);
    } catch (Throwable e) {
      logger.error("Unexpected exception on resetting isolation level of JDBC Connection after transaction", e);
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy