Ibatis中关于事务的一个疑问的刨析——startTransaction的时候到底有没有与数据库交互。

在Ibatis中我们要执行一个事务,代码是这样的:

 

sqlMapClient.startTransaction ();   

sqlMapClient.add (xxxxx);   
sqlMapClient.update (xxxxx);   

sqlMapClient.commitTransaction ();  
看到这段代码你会不会有一个问题,sqlMapClient里面到底是怎么做的? 我能猜到有两种做法:

1. 调用sqlMapClient.startTransaction时会通知数据库事务开始了,然后依次执行add和update,最后告诉数据库提交事务。

2.调用sqlMapClient.startTransaction时并没有通知数据库,只不过在sqlMapClient这个对象中记录了一个标志,然后下面的add操作与update操作也没有实际上执行语句,只是产生了sql语句并存储在sqlMapClient中。最终调用sqlMapClient.commitTransaction时,会把产生的这些sql语句作为一个事务一次提交执行。

 

google了一下,没发现有人提出这样的问题。只好自己看源码。下面是源码的调用关系。

SqlMapClientImpl.startTransaction() --->

  public void startTransaction(int transactionIsolation) throws SQLException {
    getLocalSqlMapSession().startTransaction(transactionIsolation);
  }
这里的 getLocalSqlMapSession()返回了一个SqlMapSession对象,这个对象非常重要,它管理了一次事务的状态,为什么重要后面会讲到。

继续跟踪调用

------>SqlMapSessionImpl.startTransaction(transactionIsolation)

------>SqlMapExecutorDelegate.startTransaction(session, transactionIsolation)

------>TransactionManager.begin(session, transactionIsolation)这个方法是重点,看代码:

 public void begin(SessionScope session, int transactionIsolation) throws SQLException, TransactionException {
    Transaction trans = session.getTransaction();
    TransactionState state = session.getTransactionState();
    if (state == TransactionState.STATE_STARTED) {
      throw new TransactionException("TransactionManager could not start a new transaction.  " +
          "A transaction is already started.");
    } else if (state == TransactionState.STATE_USER_PROVIDED) {
      throw new TransactionException("TransactionManager could not start a new transaction.  " +
          "A user provided connection is currently being used by this session.  " +
          "The calling .setUserConnection (null) will clear the user provided transaction.");
    }

    txThrottle.increment();

    try {
      trans = transactionConfig.newTransaction(transactionIsolation);
-------------------------------------------------------------------
      session.setCommitRequired(false);
    } catch (SQLException e) {
      txThrottle.decrement();
      throw e;
    } catch (TransactionException e) {
      txThrottle.decrement();
      throw e;
    }

    session.setTransaction(trans);
----------------------------------------------
    session.setTransactionState(TransactionState.STATE_STARTED);
  }
  注意用横线标出的那两句,首先创建了一个Transcaction,然后把它放入了session,为什么放入session,很重要,继续往下看。

transactionConfig.newTransaction(transactionIsolation);中创建了一个JdbcTransaction

看看JdbcTransaction的代码:

public class JdbcTransaction implements Transaction {

  private static final Log connectionLog = LogFactory.getLog(Connection.class);

  private DataSource dataSource;
  private Connection connection;
  private IsolationLevel isolationLevel = new IsolationLevel();

  public JdbcTransaction(DataSource ds, int isolationLevel) throws TransactionException {
    // Check Parameters
    dataSource = ds;
    if (dataSource == null) {
      throw new TransactionException("JdbcTransaction initialization failed.  DataSource was null.");
    }
    this.isolationLevel.setIsolationLevel(isolationLevel);
  }

  private void init() throws SQLException, TransactionException {
    // Open JDBC Transaction
    connection = dataSource.getConnection();
    if (connection == null) {
      throw new TransactionException("JdbcTransaction could not start transaction.  Cause: The DataSource returned a null connection.");
    }
    // Isolation Level
    isolationLevel.applyIsolationLevel(connection);
    // AutoCommit
    if (connection.getAutoCommit()) {
      connection.setAutoCommit(false);
    }
    // Debug
    if (connectionLog.isDebugEnabled()) {
      connection = ConnectionLogProxy.newInstance(connection);
    }
  }

  public void commit() throws SQLException, TransactionException {
    if (connection != null) {
      connection.commit();
    }
  }

  public void rollback() throws SQLException, TransactionException {
    if (connection != null) {
      connection.rollback();
    }
  }

  public void close() throws SQLException, TransactionException {
    if (connection != null) {
      try {
        isolationLevel.restoreIsolationLevel(connection);
      } finally {
        connection.close();
        connection = null;
      }
    }
  }

  public Connection getConnection() throws SQLException, TransactionException {
    if (connection == null) {
      init();
    }
    return connection;
  }

}
它在Init方法中调用了 connection.setAutoCommit(false); 来开始一个事务。 但是Init方法并没有在构造方法中调用啊。那它是什么时候被调用的呢? 仔细看一下,是在 getConnection() 方法中调用。那 getConnection() 又是什么时候被调用呢。回头想一下,前面我们把这个创建出来的transcation对象放入了session,在事务中第一次操作数据库的时候,比如说add操作时,就会从session中把这个transcation对象拿出来,然后调用 getConnection()方法 。这时就会通知数据库开始一个事务。 到现在为止,我们的问题应该清楚了。我刚开始提出来的两个猜想都不完全正确。不过第一个猜想更接近一点。 正确的答案是: 调用sqlMapClient.startTransaction时并没有通知数据库开始事务。但也不是最后commit时一次提交。 而是在 调用sqlMapClient.startTransaction后,第一次执行数据库操作时通知数据库开始事务,在我们的例子中,就是 sqlMapClient.add (xxxxx)的这个时机。   

猜你喜欢

转载自spark-li.iteye.com/blog/1838347