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
的代码:
SqlSessionFactory#openSessionFromDataSource()
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
Transaction
这个类算是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