View Javadoc
1   /**
2    *    Copyright 2009-2019 the original author or authors.
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
15   */
16  package org.apache.ibatis.transaction.jdbc;
17  
18  import java.sql.Connection;
19  import java.sql.SQLException;
20  import javax.sql.DataSource;
21  
22  import org.apache.ibatis.logging.Log;
23  import org.apache.ibatis.logging.LogFactory;
24  import org.apache.ibatis.session.TransactionIsolationLevel;
25  import org.apache.ibatis.transaction.Transaction;
26  import org.apache.ibatis.transaction.TransactionException;
27  
28  /**
29   * {@link Transaction} that makes use of the JDBC commit and rollback facilities directly.
30   * It relies on the connection retrieved from the dataSource to manage the scope of the transaction.
31   * Delays connection retrieval until getConnection() is called.
32   * Ignores commit or rollback requests when autocommit is on.
33   *
34   * @author Clinton Begin
35   *
36   * @see JdbcTransactionFactory
37   */
38  public class JdbcTransaction implements Transaction {
39  
40    private static final Log log = LogFactory.getLog(JdbcTransaction.class);
41  
42    protected Connection connection;
43    protected DataSource dataSource;
44    protected TransactionIsolationLevel level;
45    protected boolean autoCommit;
46  
47    public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
48      dataSource = ds;
49      level = desiredLevel;
50      autoCommit = desiredAutoCommit;
51    }
52  
53    public JdbcTransaction(Connection connection) {
54      this.connection = connection;
55    }
56  
57    @Override
58    public Connection getConnection() throws SQLException {
59      if (connection == null) {
60        openConnection();
61      }
62      return connection;
63    }
64  
65    @Override
66    public void commit() throws SQLException {
67      if (connection != null && !connection.getAutoCommit()) {
68        if (log.isDebugEnabled()) {
69          log.debug("Committing JDBC Connection [" + connection + "]");
70        }
71        connection.commit();
72      }
73    }
74  
75    @Override
76    public void rollback() throws SQLException {
77      if (connection != null && !connection.getAutoCommit()) {
78        if (log.isDebugEnabled()) {
79          log.debug("Rolling back JDBC Connection [" + connection + "]");
80        }
81        connection.rollback();
82      }
83    }
84  
85    @Override
86    public void close() throws SQLException {
87      if (connection != null) {
88        resetAutoCommit();
89        if (log.isDebugEnabled()) {
90          log.debug("Closing JDBC Connection [" + connection + "]");
91        }
92        connection.close();
93      }
94    }
95  
96    protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
97      try {
98        if (connection.getAutoCommit() != desiredAutoCommit) {
99          if (log.isDebugEnabled()) {
100           log.debug("Setting autocommit to " + desiredAutoCommit + " on JDBC Connection [" + connection + "]");
101         }
102         connection.setAutoCommit(desiredAutoCommit);
103       }
104     } catch (SQLException e) {
105       // Only a very poorly implemented driver would fail here,
106       // and there's not much we can do about that.
107       throw new TransactionException("Error configuring AutoCommit.  "
108           + "Your driver may not support getAutoCommit() or setAutoCommit(). "
109           + "Requested setting: " + desiredAutoCommit + ".  Cause: " + e, e);
110     }
111   }
112 
113   protected void resetAutoCommit() {
114     try {
115       if (!connection.getAutoCommit()) {
116         // MyBatis does not call commit/rollback on a connection if just selects were performed.
117         // Some databases start transactions with select statements
118         // and they mandate a commit/rollback before closing the connection.
119         // A workaround is setting the autocommit to true before closing the connection.
120         // Sybase throws an exception here.
121         if (log.isDebugEnabled()) {
122           log.debug("Resetting autocommit to true on JDBC Connection [" + connection + "]");
123         }
124         connection.setAutoCommit(true);
125       }
126     } catch (SQLException e) {
127       if (log.isDebugEnabled()) {
128         log.debug("Error resetting autocommit to true "
129             + "before closing the connection.  Cause: " + e);
130       }
131     }
132   }
133 
134   protected void openConnection() throws SQLException {
135     if (log.isDebugEnabled()) {
136       log.debug("Opening JDBC Connection");
137     }
138     connection = dataSource.getConnection();
139     if (level != null) {
140       connection.setTransactionIsolation(level.getLevel());
141     }
142     setDesiredAutoCommit(autoCommit);
143   }
144 
145   @Override
146   public Integer getTimeout() throws SQLException {
147     return null;
148   }
149 
150 }