在 MyBatis 中有两种事务管理器类型,也就是 type="[JDBC|MANAGED]":
JDBC – 这个配置直接简单使用了 JDBC 的提交和回滚设置。它依赖于从数据源得到的连接来管理事务范围。
MANAGED – 这个配置几乎没做什么。它从来不提交或回滚一个连接。而它会让容器来管理事务的整个生命周期(比如 Spring 或 JEE 应用服务器的上下文)。默认情况下它会关闭连接。然而一些容器并不希望这样,因此如果你需要从连接中停止它,将 closeConnection 属性设置为 false。例如:
<transactionManager type="MANAGED"> <property name="closeConnection" value="false"/> </transactionManager>
这两种事务管理器都不需要任何属性。
其实,JDBC 类型对应 JdbcTransactionFactory、JdbcTransaction;MANAGED 类型对应 ManagedTransactionFactory、ManagedTransaction。它们分别实现了 TransactionFactory 和 Transaction 接口。如下图:
你要自定义事务管理器需要实现 TransactionFactory 和 Transaction 接口。
(1)TransactionFactory 接口源码如下:
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 */ 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 */ Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit); }
该接口定义了三个方法,一个用来设置属性,另外两个用来创建事务对象。
(2)Transaction 接口源码如下:
public interface Transaction { /** * Retrieve inner database connection * @return DataBase connection * @throws SQLException */ Connection getConnection() throws SQLException; /** * Commit inner database connection. * @throws SQLException */ void commit() throws SQLException; /** * Rollback inner database connection. * @throws SQLException */ void rollback() throws SQLException; /** * Close inner database connection. * @throws SQLException */ void close() throws SQLException; }
该接口定义了四个方法,分别用于获取数据库连接、提交事务、回滚事务、关闭数据库连接。
当我们实现了上面两个接口,需要在别名中进行声明,例如:
<typeAliases> <!-- 声明自定义的事务管理器 --> <typeAlias alias="my_transaction" type="com.hxstrive.mybatis.transaction.demo1.MyTransactionFactory" /> </typeAliases>
然后在 <transactionManager> 标签的 type 属性中指定定义事务管理器别名,如:my_transaction
<transactionManager type="my_transaction" />
我们配置类型别名,MyBatis 是怎样发现我们自定义的事务管理器的呢?查看源码就可以知道了,如下图:
进入 resolveClass 方法,该方法将调用 resolveAlias 方法去解析别名(这里别名为 my_transaction),如下:
resolveAlias 方法中将从 TYPE_ALIASES Map中根据 key 获取别名对应的类全限定名称。如下:
(1)MyBatis 配置文件 mybatis-cfg.xml 内容如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <properties resource="database.properties"/> <typeAliases> <!-- 声明自定义的事务管理器 --> <typeAlias alias="my_transaction" type="com.hxstrive.mybatis.transaction.demo1.MyTransactionFactory" /> </typeAliases> <environments default="MySqlDatabase" > <environment id="MySqlDatabase" > <!-- 使用自定义事务管理器 --> <transactionManager type="my_transaction" /> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/hxstrive/mybatis/transaction/demo1/UserMapper.xml" /> </mappers> </configuration>
(2)自定义事务工厂类,代码如下:
package com.hxstrive.mybatis.transaction.demo1; import org.apache.ibatis.session.TransactionIsolationLevel; import org.apache.ibatis.transaction.Transaction; import org.apache.ibatis.transaction.TransactionFactory; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; public class MyTransactionFactory implements TransactionFactory { private static final String NAME = MyTransactionFactory.class.getSimpleName(); @Override public void setProperties(Properties props) { System.out.println(NAME + " setProperties()"); } @Override public Transaction newTransaction(Connection conn) { System.out.println(NAME + " newTransaction()"); return new MyTransaction(conn); } @Override public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) { System.out.println(NAME + " newTransaction()"); try { return new MyTransaction(dataSource.getConnection()); } catch (SQLException e) { e.printStackTrace(); } return null; } }
(3)自定义事务类,代码如下:
package com.hxstrive.mybatis.transaction.demo1; import org.apache.ibatis.transaction.Transaction; import java.sql.Connection; import java.sql.SQLException; public class MyTransaction implements Transaction { private static final String NAME = MyTransaction.class.getSimpleName(); private Connection connection; public MyTransaction(Connection connection) { this.connection = connection; } @Override public Connection getConnection() throws SQLException { System.out.println(NAME + " getConnection()"); return this.connection; } @Override public void commit() throws SQLException { System.out.println(NAME + " commit()"); if(null != this.connection && !this.connection.getAutoCommit()) { this.connection.commit(); } } @Override public void rollback() throws SQLException { System.out.println(NAME + " rollback()"); if(null != this.connection && !this.connection.getAutoCommit()) { this.connection.rollback(); } } @Override public void close() throws SQLException { System.out.println(NAME + " close()"); if(null != this.connection) { this.connection.close(); } } }
(3)定义 Mapper 接口和配置文件
a、UserMapper.java
package com.hxstrive.mybatis.transaction.demo1; public interface UserMapper { /** 查询用户数 */ int getUserCount(); }
b、UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.hxstrive.mybatis.transaction.demo1.UserMapper"> <!-- 查询用户数 --> <select id="getUserCount" resultType="integer"> select count(*) from `user` </select> </mapper>
(4)客户端代码如下:
package com.hxstrive.mybatis.transaction.demo1; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.InputStream; public class TransactionDemo { public static void main(String[] args) throws Exception { String cfgName = "com/hxstrive/mybatis/transaction/demo1/mybatis-cfg.xml"; InputStream input = Resources.getResourceAsStream(cfgName); SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlFactory = factoryBuilder.build(input); SqlSession sqlSession = sqlFactory.openSession(true); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); int userCount = userMapper.getUserCount(); System.out.println("用户总数=" + userCount); sqlSession.close(); } }
运行客户端代码,输出结果如下:
MyTransactionFactory setProperties() 2020-09-13 15:59:08,269 DEBUG [org.apache.ibatis.logging.LogFactory] - Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter. 2020-09-13 15:59:08,316 DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] - PooledDataSource forcefully closed/removed all connections. 2020-09-13 15:59:08,317 DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] - PooledDataSource forcefully closed/removed all connections. 2020-09-13 15:59:08,317 DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] - PooledDataSource forcefully closed/removed all connections. 2020-09-13 15:59:08,317 DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] - PooledDataSource forcefully closed/removed all connections. MyTransactionFactory newTransaction() 2020-09-13 15:59:08,934 DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] - Created connection 1555690610. MyTransaction getConnection() 2020-09-13 15:59:08,971 DEBUG [com.hxstrive.mybatis.transaction.demo1.UserMapper.getUserCount] - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@5cb9f472] 2020-09-13 15:59:08,972 DEBUG [com.hxstrive.mybatis.transaction.demo1.UserMapper.getUserCount] - ==> Preparing: select count(*) from `user` 2020-09-13 15:59:09,080 DEBUG [com.hxstrive.mybatis.transaction.demo1.UserMapper.getUserCount] - ==> Parameters: 2020-09-13 15:59:09,138 DEBUG [com.hxstrive.mybatis.transaction.demo1.UserMapper.getUserCount] - <== Total: 1 用户总数=3 MyTransaction close() 2020-09-13 15:59:09,139 DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] - Returned connection 1555690610 to pool.