mybatis事务管理机制详解
1.mybatis事务的配置和使用
mybatis事务有两种使用方式:
(a):使用JDBC的事务管理机制:即使用java.Sql.Connection对象完成对事务的提交,回滚和关闭操作。
(b):使用MANAGED的事务管理机制:mybatis本身不会去实现事务管理的相关操作,而是交个外部容器来管理事务。当与spring整合使用后,一般使用spring来管理事务。
在配置文件中的配置:
<environments default="development"> <environment id="development">
//JDBC的事务方式 <transactionManager type="JDBC" />
<dataSource type="POOLED"> <property name="driver" value="${driver}" /> <property name="url" value="${url}" /> <property name="username" value="${username}" /> <property name="password" value="${password}" /> </dataSource> </environment> </environments>
在这段配置信息中,有三个节点:<transactionManager />,<dataSource>,<environment>。其中,<dataSource>节点封装了数据源的信息,<environment>封装了数据库的连接,包括数据源和事务,然后把该节点信息封装到了Configuration对象中,方便后面使用,现在看下mybatis在初始化加载配置文件时,对<environment/>节点的解析:
private void environmentsElement(XNode context) throws Exception { if (context != null) { if (environment == null) { environment = context.getStringAttribute("default"); } for (XNode child : context.getChildren()) { String id = child.getStringAttribute("id"); if (isSpecifiedEnvironment(id)) {
// 解析 <transactionManager>节点,获取事务工厂对象 进入该方法 TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
// 解析<DataSource>节点,获取数据源对象 DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); DataSource dataSource = dsFactory.getDataSource(); Environment.Builder environmentBuilder = new Environment.Builder(id) .transactionFactory(txFactory) .dataSource(dataSource); configuration.setEnvironment(environmentBuilder.build()); } } } }
进入解析<transactionManager/>节点的方法:
private TransactionFactory transactionManagerElement(XNode context) throws Exception { if (context != null) {
// 获取type的属性值,JDBC或者MANAGED String type = context.getStringAttribute("type"); Properties props = context.getChildrenAsProperties();
//生成一个TransactionFactory实例 TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance(); factory.setProperties(props); return factory; } throw new BuilderException("Environment declaration requires a TransactionFactory."); }
2.事务工厂TransactionFactory
TransactionFactory定义了创建Transaction的两种方法,如下:
public interface TransactionFactory { /** * Sets transaction factory custom properties. * @param props */ void setProperties(Properties props); /** * Creates a {@link Transaction} out of an existing connection. * @param conn Existing database connection * @return Transaction * @since 3.1.0
* 方法一:通过指定的Connection对象来创建事务 */ Transaction newTransaction(Connection conn); /** * Creates a {@link Transaction} out of a datasource. * @param dataSource DataSource to take the connection from * @param level Desired isolation level * @param autoCommit Desired autocommit * @return Transaction * @since 3.1.0
* 方法二:通过DataSource,隔离级别,是否自动提交 来创建事务 */ Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit); }
TransactionFactory有两个子类:JdbcTransactionFactory和ManagedTransactionFactory
2.1 JdbcTransactionFactory,会创建JDBC类型的事务,就是JdbcTransaction,看下源码:
public class JdbcTransactionFactory implements TransactionFactory { public void setProperties(Properties props) { } // JDBCTransaction的创建有两种方法 public Transaction newTransaction(Connection conn) { return new JdbcTransaction(conn); } public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) { return new JdbcTransaction(ds, level, autoCommit); } }
2.2 ManagedTransactionFactory,会创建MANAGED类型的事务,ManagedTransaction,同样有两种方法:
public class ManagedTransactionFactory implements TransactionFactory { private boolean closeConnection = true; public void setProperties(Properties props) { if (props != null) { String closeConnectionProperty = props.getProperty("closeConnection"); if (closeConnectionProperty != null) { closeConnection = Boolean.valueOf(closeConnectionProperty); } } } public Transaction newTransaction(Connection conn) { return new ManagedTransaction(conn, closeConnection); } public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) { // Silently ignores autocommit and isolation level, as managed transactions are entirely // controlled by an external manager. It's silently ignored so that // code remains portable between managed and unmanaged configurations. return new ManagedTransaction(ds, level, closeConnection); } }
3.JdbcTransaction和ManagedTransaction
3.1 JdbcTransaction
从源码可知:jdbcTransaction从DataSource获取连接对象Connection,然后利用Connection对象管理事务的commit和rollback,实际在事务处理上,jdbcTransaction是对java.sql.Connection的一个包装,它是使用Connection对象来管理事务的。
public class JdbcTransaction implements Transaction { private static final Log log = LogFactory.getLog(JdbcTransaction.class); protected Connection connection;// 连接对象 protected DataSource dataSource; //数据源 protected TransactionIsolationLevel level; // 隔离级别 protected boolean autoCommmit; //是否自动提交 public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) { dataSource = ds; level = desiredLevel; autoCommmit = desiredAutoCommit; } public JdbcTransaction(Connection connection) { this.connection = connection; }
// 获取连接对象 public Connection getConnection() throws SQLException { if (connection == null) { openConnection(); } return connection; } // 使用Connection对象提交 public void commit() throws SQLException { if (connection != null && !connection.getAutoCommit()) { if (log.isDebugEnabled()) { log.debug("Committing JDBC Connection [" + connection + "]"); } connection.commit(); } } public void rollback() throws SQLException { if (connection != null && !connection.getAutoCommit()) { if (log.isDebugEnabled()) { log.debug("Rolling back JDBC Connection [" + connection + "]"); } connection.rollback(); } } public void close() throws SQLException { if (connection != null) { resetAutoCommit(); if (log.isDebugEnabled()) { log.debug("Closing JDBC Connection [" + connection + "]"); } connection.close(); } } protected void setDesiredAutoCommit(boolean desiredAutoCommit) { try { if (connection.getAutoCommit() != desiredAutoCommit) { if (log.isDebugEnabled()) { log.debug("Setting autocommit to " + desiredAutoCommit + " on JDBC Connection [" + connection + "]"); } connection.setAutoCommit(desiredAutoCommit); } } catch (SQLException e) { // Only a very poorly implemented driver would fail here, // and there's not much we can do about that. throw new TransactionException("Error configuring AutoCommit. " + "Your driver may not support getAutoCommit() or setAutoCommit(). " + "Requested setting: " + desiredAutoCommit + ". Cause: " + e, e); } } protected void resetAutoCommit() { try { if (!connection.getAutoCommit()) { // MyBatis does not call commit/rollback on a connection if just selects were performed. // Some databases start transactions with select statements // and they mandate a commit/rollback before closing the connection. // A workaround is setting the autocommit to true before closing the connection. // Sybase throws an exception here. if (log.isDebugEnabled()) { log.debug("Resetting autocommit to true on JDBC Connection [" + connection + "]"); } connection.setAutoCommit(true); } } catch (SQLException e) { log.debug("Error resetting autocommit to true " + "before closing the connection. Cause: " + e); } } protected void openConnection() throws SQLException { if (log.isDebugEnabled()) { log.debug("Opening JDBC Connection"); }
// 从数据源获取连接对象 connection = dataSource.getConnection(); if (level != null) { connection.setTransactionIsolation(level.getLevel()); } setDesiredAutoCommit(autoCommmit); } }
3.2 ManagedTransaction
ManagedTransaction对事务的commit和rollback交给了容器去管理,自己本身并没有做任何处理,看源码:
public class ManagedTransaction implements Transaction { private static final Log log = LogFactory.getLog(ManagedTransaction.class); private DataSource dataSource; private TransactionIsolationLevel level; private Connection connection; private boolean closeConnection; public Connection getConnection() throws SQLException { if (this.connection == null) { openConnection(); } return this.connection; }
// 没有做任何处理,因为交给了外部容器去做这件事了 public void commit() throws SQLException { // Does nothing } public void rollback() throws SQLException { // Does nothing } public void close() throws SQLException { if (this.closeConnection && this.connection != null) { if (log.isDebugEnabled()) { log.debug("Closing JDBC Connection [" + this.connection + "]"); } this.connection.close(); } } protected void openConnection() throws SQLException { if (log.isDebugEnabled()) { log.debug("Opening JDBC Connection"); } this.connection = this.dataSource.getConnection(); if (this.level != null) { this.connection.setTransactionIsolation(this.level.getLevel()); } } }