2.3.2 SqlSession 生命周期
SqlSessions 是由 SqlSessionFactory 实例创建的;而 SqlSessionFactory
是由 SqlSessionFactoryBuilder 创建的。
???? 注意:当 MyBatis 与一些依赖注入框架(如 Spring 或者 Guice)同时使用时,SqlSessions 将被依赖注入框架所创建,
所以你不需要使用 SqlSessionFactoryBuilder 或者 SqlSessionFactory。
每个线程都应该有它自己的 SqlSession 实例。
SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。正确在 Web 中使用 SqlSession 的场景是:每次收到的 HTTP 请求,就可以打开一个 SqlSession,返回一个响应,就关闭它。
编程模式:
try (SqlSession session = sqlSessionFactory.openSession) { // 你的应用逻辑代码}
2.4. 映射器
2.4.1 映射器职责
映射器是一些由用户创建的、绑定 SQL 语句的接口。
SqlSession 中的 insert、update、delete 和 select 方法都很强大,但也有些繁琐。更通用的方式是使用映射器类来执行映射语句。一个映射器类就是一个仅需声明与 SqlSession 方法相匹配的方法的接口类。
MyBatis 将配置文件中的每一个 <mapper> 节点抽象为一个 Mapper 接口,而这个接口中声明的方法和跟 <mapper> 节点中的 <select|update|delete|insert> 节点相对应,即 <select|update|delete|insert> 节点的 id 值为 Mapper 接口中的方法名称,parameterType 值表示 Mapper 对应方法的入参类型,而 resultMap 值则对应了 Mapper 接口表示的返回值类型或者返回结果集的元素类型。
MyBatis 会根据相应的接口声明的方法信息,通过动态代理机制生成一个 Mapper 实例;MyBatis 会根据这个方法的方法名和参数类型,确定 Statement id,然后和 SqlSession 进行映射,底层还是通过 SqlSession 完成和数据库的交互。
下面的示例展示了一些方法签名以及它们是如何映射到 SqlSession 上的。

注意:
映射器接口不需要去实现任何接口或继承自任何类。只要方法可以被唯一标识对应的映射语句就可以了。
映射器接口可以继承自其他接口。当使用 XML 来构建映射器接口时要保证语句被包含在合适的命名空间中。而且,唯一的限制就是你不能在两个继承关系的接口中拥有相同的方法签名(潜在的危险做法不可取)。
2.4.2 映射器生命周期
映射器接口的实例是从 SqlSession 中获得的。因此从技术层面讲,任何映射器实例的最大作用域是和请求它们的 SqlSession 相同的。尽管如此,映射器实例的最佳作用域是方法作用域。也就是说,映射器实例应该在调用它们的方法中被请求,用过之后即可丢弃。
编程模式:
try (SqlSession session = sqlSessionFactory.openSession) { BlogMapper mapper = session.getMapper(BlogMapper.class); // 你的应用逻辑代码}
映射器注解
MyBatis 是一个 XML 驱动的框架。配置信息是基于 XML 的,而且映射语句也是定义在 XML 中的。MyBatis 3 以后,支持注解配置。注解配置基于配置 API;而配置 API 基于 XML 配置。
MyBatis 支持诸如 @Insert、@Update、@Delete、@Select、@Result 等注解。
详细内容请参考:MyBatis 官方文档之 sqlSessions,其中列举了 MyBatis 支持的注解清单,以及基本用法。
三、 MyBatis 的架构
从 MyBatis 代码实现的角度来看,MyBatis 的主要组件有以下几个:
SqlSession - 作为 MyBatis 工作的主要顶层 API,表示和数据库交互的会话,完成必要数据库增删改查功能。
Executor - MyBatis 执行器,是 MyBatis 调度的核心,负责 SQL 语句的生成和查询缓存的维护。
StatementHandler - 封装了 JDBC Statement 操作,负责对 JDBC statement 的操作,如设置参数、将 Statement 结果集转换成 List 集合。
ParameterHandler - 负责对用户传递的参数转换成 JDBC Statement 所需要的参数。
ResultSetHandler - 负责将 JDBC 返回的 ResultSet 结果集对象转换成 List 类型的集合。
TypeHandler - 负责 java 数据类型和 jdbc 数据类型之间的映射和转换。
MappedStatement - MappedStatement 维护了一条 <select|update|delete|insert> 节点的封装。
SqlSource - 负责根据用户传递的 parameterObject,动态地生成 SQL 语句,将信息封装到 BoundSql 对象中,并返回。
BoundSql - 表示动态生成的 SQL 语句以及相应的参数信息。
Configuration - MyBatis 所有的配置信息都维持在 Configuration 对象之中。
这些组件的架构层次如下:

3.1. 配置层
配置层决定了 MyBatis 的工作方式。
MyBatis 提供了两种配置方式:
基于 XML 配置文件的方式
基于 Java API 的方式
SqlSessionFactoryBuilder 会根据配置创建 SqlSessionFactory ;SqlSessionFactory 负责创建 SqlSessions 。
3.2. 接口层
接口层负责和数据库交互的方式。MyBatis 和数据库的交互有两种方式:
1)使用 SqlSession:SqlSession 封装了所有执行语句,获取映射器和管理事务的方法。
用户只需要传入 Statement Id 和查询参数给 SqlSession 对象,就可以很方便的和数据库进行交互。
这种方式的缺点是不符合面向对象编程的范式。
2)使用 Mapper 接口:MyBatis 会根据相应的接口声明的方法信息,通过动态代理机制生成一个 Mapper 实例;MyBatis 会根据这个方法的方法名和参数类型,确定 Statement Id,然后和 SqlSession 进行映射,底层还是通过 SqlSession 完成和数据库的交互。
3.3. 数据处理层
数据处理层可以说是 MyBatis 的核心,从大的方面上讲,它要完成两个功能:
1)根据传参 Statement 和参数构建动态 SQL 语句
动态语句生成可以说是 MyBatis 框架非常优雅的一个设计,MyBatis 通过传入的参数值,使用 Ognl 来动态地构造 SQL 语句,使得 MyBatis 有很强的灵活性和扩展性。
参数映射指的是对于 java 数据类型和 jdbc 数据类型之间的转换:这里有包括两个过程:查询阶段,我们要将 java 类型的数据,转换成 jdbc 类型的数据,通过 preparedStatement.setXXX 来设值;另一个就是对 resultset 查询结果集的 jdbcType 数据转换成 java 数据类型。
2)执行 SQL 语句以及处理响应结果集 ResultSet
动态 SQL 语句生成之后,MyBatis 将执行 SQL 语句,并将可能返回的结果集转换成 List<E> 列表。
MyBatis 在对结果集的处理中,支持结果集关系一对多和多对一的转换,并且有两种支持方式,一种为嵌套查询语句的查询,还有一种是嵌套结果集的查询。
3.4. 框架支撑层
1) 事务管理机制 - MyBatis 将事务抽象成了 Transaction 接口。MyBatis 的事务管理分为两种形式:
使用 JDBC 的事务管理机制:即利用 java.sql.Connection 对象完成对事务的提交(commit)、回滚(rollback)、关闭(close)等。
使用 MANAGED 的事务管理机制:MyBatis 自身不会去实现事务管理,而是让程序的容器如(JBOSS,Weblogic)来实现对事务的管理。
2) 连接池管理
3) SQL 语句的配置 - 支持两种方式:
xml 配置
注解配置
4) 缓存机制 - MyBatis 采用两级缓存结构;
一级缓存是 Session 会话级别的缓存 - 一级缓存又被称之为本地缓存。一般而言,一个 SqlSession 对象会使用一个 Executor 对象来完成会话操作,Executor 对象会维护一个 Cache 缓存,以提高查询性能。
一级缓存的生命周期是 Session 会话级别的。
二级缓存是 Application 应用级别的缓存 - 用户配置了 "cacheEnabled=true",才会开启二级缓存。
如果开启了二级缓存,SqlSession 会先使用 CachingExecutor 对象来处理查询请求。CachingExecutor 会在二级缓存中查看是否有匹配的数据,如果匹配,则直接返回缓存结果;如果缓存中没有,再交给真正的 Executor 对象来完成查询,之后 CachingExecutor 会将真正 Executor 返回的查询结果放置到缓存中,然后在返回给用户。
二级缓存的生命周期是应用级别的。

四、SqlSession 内部工作机制
从前文,我们已经了解了,MyBatis 封装了对数据库的访问,把对数据库的会话和事务控制放到了 SqlSession 对象中。那么具体是如何工作的呢?接下来,我们通过源码解读来进行分析。
