public List<Country> selectById()throws IOException { try (Reader reader = Resources.getResourceAsReader("mybatis-config.xml")) { //创建SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); //获取SqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); //执行Sql return sqlSession.selectList("selectAll"); } }
上面是使用 MyBatis
执行一段 SQL
完整的 Java API
。
可以看到整个过程中创建了4个对象:
Reader
: 用来读取 MyBatis
的配置文件流 SqlSessionFactory
: 用来根据各种配置创建 SqlSession
,一般应该作为 static
成员变量存在,只初始化一次 SqlSession
: 真正的干活的类
对于 SqlSessionFactoryBuilder.builder()
,这里只用简单说说,此类是用来读取 MyBatis
的 XML
配置文件,然后根据配置文件来初始化 Configuration
对象,目前只用知道这个,当需要了解细节的时候再细看。
因此,对于 MyBatis
的秘密,全都藏在了 SqlSession
中。
首先看看 SqlSession
的创建方法: SqlSessionFactory#openSession()
由官方文档中可以知道, openSession()
包括以下几种重载:
//默认,level 使用数据库默认,autoCommit 为false ,execType 为defaultExecType 默认为SIMPLE //通过配置的DataSource 获取Connection 对象 SqlSession openSession() //指定是否自动提交 SqlSession openSession(boolean autoCommit) //通过connection 初始化 SqlSession openSession(Connection connection) //指定事务级别 SqlSession openSession(TransactionIsolationLevel level) //指定执行器类型和事务级别 SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) SqlSession openSession(ExecutorType execType) SqlSession openSession(ExecutorType execType, boolean autoCommit) //通过connection初始化,并指定事务级别 SqlSession openSession(ExecutorType execType, Connection connection)
也就是说,对于 SqlSession
,我们可以直接配置的属性有:
ExecutorType
: 执行器类型 AutoCommit
: 是否开启事务 ( false
为开, true
为不开) TransactionIsolationLevel
: 事务级别
接下来看看创建 SqlSession
的代码:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { //获取配置文件的environment对象 final Environment environment = configuration.getEnvironment(); //从environment对象中获取事务管理器创建对象 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); //创建事务管理器 tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); //创建执行器 final Executor executor = configuration.newExecutor(tx, execType); //将执行器作为参数创建SqlSession return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
总结起来分为3步:
environment
对象 environment
对象创建事务管理器工厂以及通过工厂创建 Transaction
Transaction
对象作为参数创建执行器 SqlSession
构造参数创建 SqlSession
首先我们看 environment
的配置:
<environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/new_test"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments>
这里配置了两个元素: transactionManager
以及 dataSource
这个类算是 MyBaits
中比较重要的一个类,重要在哪里呢?它的实现特别简单,但是在 MyBatis
中起着一个中间层的作用,首先看这个接口包含的方法:
public interface Transaction { Connection getConnection() throws SQLException; void commit() throws SQLException; void rollback() throws SQLException; //.... }
可以看出来这些功能其实就是对 Connection
的包装。
在 MyBatis
中, Transaction
有两个实现类: JdbcTransaction
和 ManagedTransaction
对于 JdbcTransaction
来说,其相当于 connection
的代理类,需要提交的时候便调用 Connection
的提交,需要回滚的时候便调用 Connection
的回滚方法
而对于 ManagedTransaction
来说,缺忽略了 commit()
和 rollback()
方法,其实现类中什么都没做。
在 MyBatis
的官方文档中,写明了:
JDBC
– 这个配置就是直接使用了 JDBC
的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。 MANAGED
– 这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE
应用服务器的上下文)
看到这里就大概能明白 MyBatis
设计 Transaction
中间层的意义了,某些时候,当 MyBatis
和其他类似 EJB
的服务整合的时候,此时不应该在 MyBatis
中涉及到提交或者回滚,而应该交由 EJB
容器管理,而这个中间层则正好可以做到一个动态选择的作用,这样可以在不修改任何代码的情况下,实现由程序本身控制事务还是交给容器管理的选择。
同理,可以想到 Spring
整合 MyBatis
,在 Spring
中,定义了事务的传播机制,而这些机制的实现,依然需要依赖 Transaction
做中间层代理,这样使用 Transaction
的代码都不用动,而实现 Spring
的事务传播则交给 Transaction
来做。
在 Spring-MyBatis
中,实现此接口的类为: SpringManagedTransaction
从这里便可以看出来面向对象的抽象的好处,如果简单的设计,可能会使得代码中到处都是 if-else
的执行判断
熟悉了 Transaction
后,应该明白它充当的作用便是 Connection
的作用,下一章来看 Executor