Mybaties高级 注解开发 延迟加载 SQL构建 Mybatis源码分析
一.Mybatis注解开发单表操作
1.1 MyBatis的常用注解
这几年来注解开发越来越流行,Mybatis也可以使用注解开发方式,这样我们就可以减少编写Mapper
映射文件了。我们先围绕一些基本的CRUD来学习,再学习复杂映射多表操作。
@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result 一起使用,封装多个结果集
@One:实现一对一结果集封装
@Many:实现一对多结果集封装
1.2 MyBatis的增删改查
我们完成简单的student表的增删改查的操作
- 步骤一:创建mapper接口
public interface StudentMapper {
//查询全部
@Select("SELECT * FROM student")
public abstract List<Student> selectAll();
//新增操作
@Insert("INSERT INTO student VALUES (#{id},#{name},#{age})")
public abstract Integer insert(Student stu);
//修改操作
@Update("UPDATE student SET name=#{name},age=#{age} WHERE id=#{id}")
public abstract Integer update(Student stu);
//删除操作
@Delete("DELETE FROM student WHERE id=#{id}")
public abstract Integer delete(Integer id);
}
- 步骤二:测试类
public class Test01 {
@Test
public void selectAll() throws Exception{
//1.加载核心配置文件
InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
//2.获取SqlSession工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//3.通过工厂对象获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//4.获取StudentMapper接口的实现类对象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
//5.调用实现类对象中的方法,接收结果
List<Student> list = mapper.selectAll();
//6.处理结果
for (Student student : list) {
System.out.println(student);
}
//7.释放资源
sqlSession.close();
is.close();
}
@Test
public void insert() throws Exception{
//1.加载核心配置文件
InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
//2.获取SqlSession工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//3.通过工厂对象获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//4.获取StudentMapper接口的实现类对象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
//5.调用实现类对象中的方法,接收结果
Student stu = new Student(4,"赵六",26);
Integer result = mapper.insert(stu);
//6.处理结果
System.out.println(result);
//7.释放资源
sqlSession.close();
is.close();
}
@Test
public void update() throws Exception{
//1.加载核心配置文件
InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
//2.获取SqlSession工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//3.通过工厂对象获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//4.获取StudentMapper接口的实现类对象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
//5.调用实现类对象中的方法,接收结果
Student stu = new Student(4,"赵六",36);
Integer result = mapper.update(stu);
//6.处理结果
System.out.println(result);
//7.释放资源
sqlSession.close();
is.close();
}
@Test
public void delete() throws Exception{
//1.加载核心配置文件
InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
//2.获取SqlSession工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//3.通过工厂对象获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//4.获取StudentMapper接口的实现类对象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
//5.调用实现类对象中的方法,接收结果
Integer result = mapper.delete(4);
//6.处理结果
System.out.println(result);
//7.释放资源
sqlSession.close();
is.close();
}
}
- 注意:
修改MyBatis的核心配置文件,我们使用了注解替代的映射文件,所以我们只需要加载使用了注解的Mapper接口即可
<mappers>
<!--扫描使用注解的类-->
<mapper class="com.itheima.mapper.UserMapper"></mapper>
</mappers>
或者指定扫描包含映射关系的接口所在的包也可以
<mappers>
<!--扫描使用注解的类所在的包-->
<package name="com.itheima.mapper"></package>
</mappers>
1.3 注解开发总结
注解可以简化开发操作,省略映射配置文件的编写。
-
常用注解
@Select(“查询的 SQL 语句”):执行查询操作注解
@Insert(“查询的 SQL 语句”):执行新增操作注解
@Update(“查询的 SQL 语句”):执行修改操作注解
@Delete(“查询的 SQL 语句”):执行删除操作注解
-
配置映射关系
<mappers> <package name="接口所在包"/> </mappers>
二.MyBatis注解开发的多表操作
2.1 MyBatis的注解实现复杂映射开发
实现复杂关系映射之前我们可以在映射文件中通过配置来实现,使用注解开发后,我们可以使用@Results注解,@Result注解,@One注解,@Many注解组合完成复杂关系的配置
2.2 一对一查询
2.2.1 一对一查询的模型
一对一查询的需求:查询一个用户信息,与此同时查询出该用户对应的身份证信息
2.2.2 一对一查询的语句
对应的sql语句:
SELECT * FROM card;
SELECT * FROM person WHERE id=#{id};
2.2.3 创建PersonMapper接口
public interface PersonMapper {
//根据id查询
@Select("SELECT * FROM person WHERE id=#{id}")
public abstract Person selectById(Integer id);
}
2.2.4 使用注解配置Mapper
public interface CardMapper {
//查询全部
@Select("SELECT * FROM card")
@Results({
@Result(column = "id",property = "id"),
@Result(column = "number",property = "number"),
@Result(
property = "p", // 被包含对象的变量名
javaType = Person.class, // 被包含对象的实际数据类型
column = "pid", // 根据查询出的card表中的pid字段来查询person表
/*
one、@One 一对一固定写法
select属性:指定调用哪个接口中的哪个方法
*/
one = @One(select = "com.itheima.one_to_one.PersonMapper.selectById")
)
})
public abstract List<Card> selectAll();
}
2.2.5 测试类
public class Test01 {
@Test
public void selectAll() throws Exception{
//1.加载核心配置文件
InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
//2.获取SqlSession工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//3.通过工厂对象获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//4.获取CardMapper接口的实现类对象
CardMapper mapper = sqlSession.getMapper(CardMapper.class);
//5.调用实现类对象中的方法,接收结果
List<Card> list = mapper.selectAll();
//6.处理结果
for (Card card : list) {
System.out.println(card);
}
//7.释放资源
sqlSession.close();
is.close();
}
}
2.2.6 一对一配置总结
@Results:封装映射关系的父注解。
Result[] value():定义了 Result 数组
@Result:封装映射关系的子注解。
column 属性:查询出的表中字段名称
property 属性:实体对象中的属性名称
javaType 属性:被包含对象的数据类型
one 属性:一对一查询固定属性
@One:一对一查询的注解。
select 属性:指定调用某个接口中的方法
2.3 一对多查询
2.3.1 一对多查询的模型
一对多查询的需求:查询一个课程,与此同时查询出该该课程对应的学生信息
2.3.2 一对多查询的语句
对应的sql语句:
SELECT * FROM classes
SELECT * FROM student WHERE cid=#{cid}
2.3.3 创建StudentMapper接口
public interface StudentMapper {
//根据cid查询student表
@Select("SELECT * FROM student WHERE cid=#{cid}")
public abstract List<Student> selectByCid(Integer cid);
}
2.3.4 使用注解配置Mapper
public interface ClassesMapper {
//查询全部
@Select("SELECT * FROM classes")
@Results({
@Result(column = "id",property = "id"),
@Result(column = "name",property = "name"),
@Result(
property = "students", // 被包含对象的变量名
javaType = List.class, // 被包含对象的实际数据类型
column = "id", // 根据查询出的classes表的id字段来查询student表
/*
many、@Many 一对多查询的固定写法
select属性:指定调用哪个接口中的哪个查询方法
*/
many = @Many(select = "com.itheima.one_to_many.StudentMapper.selectByCid")
)
})
public abstract List<Classes> selectAll();
}
2.3.5使用xml配置Mapper
2.3.6 测试类
public class Test01 {
@Test
public void selectAll() throws Exception{
//1.加载核心配置文件
InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
//2.获取SqlSession工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//3.通过工厂对象获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//4.获取ClassesMapper接口的实现类对象
ClassesMapper mapper = sqlSession.getMapper(ClassesMapper.class);
//5.调用实现类对象中的方法,接收结果
List<Classes> list = mapper.selectAll();
//6.处理结果
for (Classes cls : list) {
System.out.println(cls.getId() + "," + cls.getName());
List<Student> students = cls.getStudents();
for (Student student : students) {
System.out.println("\t" + student);
}
}
//7.释放资源
sqlSession.close();
is.close();
}
}
2.3.7 一对多配置总结
@Results:封装映射关系的父注解。
Result[] value():定义了 Result 数组
@Result:封装映射关系的子注解。
column 属性:查询出的表中字段名称
property 属性:实体对象中的属性名称
javaType 属性:被包含对象的数据类型
many 属性:一对多查询固定属性
@Many:一对多查询的注解。
select 属性:指定调用某个接口中的方法
2.4 多对多查询
2.4.1 多对多查询的模型
多对多查询的需求:查询学生以及所对应的课程信息
2.4.2 多对多查询的语句
对应的sql语句:
SELECT DISTINCT s.id,s.name,s.age FROM student s,stu_cr sc
WHERE sc.sid=s.id
SELECT c.id,c.name FROM stu_cr sc,course c
WHERE sc.cid=c.id AND sc.sid=#{id}
2.4.3 添加CourseMapper 接口方法
public interface CourseMapper {
//根据学生id查询所选课程
@Select("SELECT c.id,c.name FROM stu_cr sc,course c WHERE sc.cid=c.id AND sc.sid=#{id}")
public abstract List<Course> selectBySid(Integer id);
}
2.4.4 使用注解配置Mapper
public interface StudentMapper {
//查询全部
@Select("SELECT DISTINCT s.id,s.name,s.age FROM student s,stu_cr sc WHERE sc.sid=s.id")
@Results({
@Result(column = "id",property = "id"),
@Result(column = "name",property = "name"),
@Result(column = "age",property = "age"),
@Result(
property = "courses", // 被包含对象的变量名
javaType = List.class, // 被包含对象的实际数据类型
column = "id", // 根据查询出student表的id来作为关联条件,去查询中间表和课程表
/*
many、@Many 一对多查询的固定写法
select属性:指定调用哪个接口中的哪个查询方法
*/
many = @Many(select = "com.itheima.many_to_many.CourseMapper.selectBySid")
)
})
public abstract List<Student> selectAll();
}
2.4.5 测试类
public class Test01 {
@Test
public void selectAll() throws Exception{
//1.加载核心配置文件
InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
//2.获取SqlSession工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//3.通过工厂对象获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//4.获取StudentMapper接口的实现类对象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
//5.调用实现类对象中的方法,接收结果
List<Student> list = mapper.selectAll();
//6.处理结果
for (Student student : list) {
System.out.println(student.getId() + "," + student.getName() + "," + student.getAge());
List<Course> courses = student.getCourses();
for (Course cours : courses) {
System.out.println("\t" + cours);
}
}
//7.释放资源
sqlSession.close();
is.close();
}
}
2.4.6 多对多配置总结
@Results:封装映射关系的父注解。
Result[] value():定义了 Result 数组
@Result:封装映射关系的子注解。
column 属性:查询出的表中字段名称
property 属性:实体对象中的属性名称
javaType 属性:被包含对象的数据类型
many 属性:一对多查询固定属性
@Many:一对多查询的注解。
select 属性:指定调用某个接口中的方法
Results和Result
One和Many
三.构建sql
3.1 SQL 构建对象介绍
- 我们之前通过注解开发时,相关 SQL 语句都是自己直接拼写的。一些关键字写起来比较麻烦、而且容易出错。
- MyBatis 给我们提供了 org.apache.ibatis.jdbc.SQL 功能类,专门用于构建 SQL 语句
3.2 查询功能的实现
-
定义功能类并提供获取查询的 SQL 语句的方法。
-
@SelectProvider:生成查询用的 SQL 语句注解。
type 属性:生成 SQL 语句功能类对象
method 属性:指定调用方法
3.3 新增功能的实现
-
定义功能类并提供获取新增的 SQL 语句的方法。
-
@InsertProvider:生成新增用的 SQL 语句注解。
type 属性:生成 SQL 语句功能类对象
method 属性:指定调用方法
3.4 修改功能的实现
-
定义功能类并提供获取修改的 SQL 语句的方法。
-
@UpdateProvider:生成修改用的 SQL 语句注解。
type 属性:生成 SQL 语句功能类对象
method 属性:指定调用方法
3.5 删除功能的实现
-
定义功能类并提供获取删除的 SQL 语句的方法。
-
@DeleteProvider:生成删除用的 SQL 语句注解。
type 属性:生成 SQL 语句功能类对象
method 属性:指定调用方法
3.6 动态的where条件拼接(补充)
public static String getDynamicSelectSql(Student stu){
// SELECT * FROM student WHERE name=#{name} and age=#{age}
// return new SQL(){
// {
// SELECT("*");
// FROM("student");
// if (stu.getName() != null){
// WHERE("name = #{name}");
// }
// if (stu.getAge() != null){
// WHERE("age = #{age}");
// }
// }
// }.toString();
SQL sql = new SQL();
sql.SELECT("*");
sql.FROM("student");
if (stu.getName() != null){
sql.WHERE("name = #{name}");
}
if (stu.getAge() != null){
sql.WHERE("age = #{age}");
}
return sql.toString();
}
3.7动态的set条件拼接(补充)
public static String getDynamicUpdateSql(Student stu){
// UPDATE student SET name=#{name},age=#{age} WHERE id=#{id}
return new SQL(){
{
UPDATE("student");
if (stu.getName() != null){
SET("name=#{name}");
}
if (stu.getAge() != null){
SET("age=#{age}");
}
WHERE("id=#{id}");
}
}.toString();
}
3.8动态的where in条件拼接(补充)
public static String getDynamicSelectInSql(List<Integer> list){
//SELECT * FROM student WHERE id in (1,2,3);
//SELECT * FROM student WHERE id=1 OR id=2 OR id=3
return new SQL(){
{
SELECT("*");
FROM("student");
int size = list.size();
for (int i = 0; i < size; i++) {
WHERE("id="+list.get(i));
if (i != size-1){
OR();
}
}
}
}.toString();
}
四.延迟加载
4.1使用懒加载的条件
多表联查时不能实现懒加载,或者说根本不涉及
- 存在多表查询
- 不能使用多表联查
- 被关联的对象不会被立即使用
4.2延迟加载的实现步骤
在Mybatis核心配置文件settings中开启懒加载, lazyLoadingEnabled=true
在Mybatis的UserDao映射文件中使用新的方式配置resultMap
编写查询所有用户的statement
SQL语句为只查询所有用户的SQL,返回值还是选择resultMap,指向一个resultMap
resultMap中User的本身属性和字段映射关系不变
User中关联订单的属性:在resultMap的collection子标签中添加两个属性
select属性,值为根据用户id查询属于该用户的订单statement
column属性,值为select属性查询时需要传递的参数
一对多工作中查询策略(建议,具体情况要以需求为准)
查询多的一方(订单)的时候,不需要分步 查询,延迟加载
通常需要关联查询一的一方(用户)
查询 多 订单 的时候直接联合查询 一 用户
查询一的一方(用户)的时候,需要分步查询,延迟加载
通常不会关联查询多的一方(订单)
根据业务,在需要展示 多 订单 的时候才去查询
延迟加载思想 首先查询主要内容,后面根据业务需要是否查询关联内容
注意点
只要是多表查询,都可以使用懒加载
包含一对一、一对多和多对多
4.3延迟加载和直接加载区别
延迟加载
当我们要访问的数据量过大时,明显用缓存不太合适,因为内存容量有限 ,为了减少并发量,减少系统资源的消耗,我们让数据在需要的时候才进行加载,这时我们就用到了懒加载。
如果设置的是延迟加载,那么肯定会要生成1+N条sql语句:其中“1”是查询student的语句,“N”是根据N个student的id去查询head的N条语句。
立即加载
就是表关联的时候,查询一个对象,会把他关联的对象都查出来初始化到属性中去,这个就是立即加载,所以在查询的时候可能出现多表关联的查询语句
五.MyBatis源码分析
1、SqlSessionFactory对象的构建流程
- new SqlSessionFactoryBuilder().build()
//SqlSessionFactoryBuilder
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
//SqlSessionFactoryBuilder
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//构建一个xml解析器
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//parser.parse() 解析核心配置文件
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
- parser.parse()
//XMLConfigBuilder
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
//解析核心配置文件configuration节点下的所有内容
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
//XMLConfigBuilder
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
//解析plugins节点,获取intercptor对象
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
//XMLConfigBuilder
//解析核心配置文件中plugins节点的
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
//获取plugin节点的interceptor属性(对应的是一个类的全路径类名)
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
//基于全路径类名,反射生成Interceptor对象
//为了保证字节码获取的正确性,会去判断是否是别名配置
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();
//如果插件配置了property属性,会调用这个方法将配置的属性传递到Interceptor对象中,如果需要,可以复写setProperties方法
interceptorInstance.setProperties(properties);
//将创建的Interceptor对象,添加到InterceptorChain对象中保存
//实际是保存在了一个ArrayList集合中
configuration.addInterceptor(interceptorInstance);
}
}
}
以上我只分析了一个plugins节点的解析,其他节点解析方式和plugins是一致的,然后会将所有信息都保存在Configuration对象中
//解析动作完成后,会利用build方法,返回一个Factory对象
//而这个对象在实际开发中是要保证唯一而且长久保存的
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
后期如果需要信息,可以通过factory来找到configuration对象获取
在Configuration对象构建时,注册了很多的别名和真实数据类型的映射关系
public Configuration() {
//在核心配置文件中配置的transactionManager为JDBC,到时候实际完成事务管理的对象是JdbcTransactionFactory
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
//在核心配置文件中配置的dataSource为POOLED,到时候获取连接对象找PooledDataSourceFactory
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
}
2、Connection对象的获取和归还
PooledDataSourceFactory
//这个类是我们在核心配置文件中配置的
//他的作用,就是创建一个DataSource对象,用来提供Connection对象的
public class PooledDataSourceFactory extends UnpooledDataSourceFactory {
public PooledDataSourceFactory() {
this.dataSource = new PooledDataSource();
}
}
- PooledDataSource:这个对象是我们在进行增删改查操作是,Connection对象的真实来源(连接的获取)
//这个方法就是返回一个Connection对象的方法
@Override
public Connection getConnection() throws SQLException {
return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
}
//正真获取连接对象的代码
private PooledConnection popConnection(String username, String password) throws SQLException {
boolean countedWait = false;
PooledConnection conn = null;
long t = System.currentTimeMillis();
int localBadConnectionCount = 0;
//保证必须拿到一个连接对象并返回,除非超时异常
while (conn == null) {
//① 保证连接对象获取的线程安全
//② 当无法获取到连接对象是,需要等待(需要通过锁对象调用,同时需要将代码写在synchronized内部)
synchronized (state) {
//判断空闲的连接容器中是否还有空闲的连接对象
if (!state.idleConnections.isEmpty()) {
//如果有空闲的连接对象,直接获取
conn = state.idleConnections.remove(0);
if (log.isDebugEnabled()) {
log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
}
} else {
//没有空闲的连接对象
/*
1、判断当前正在做任务的连接数是否达到最大连接数
1、没有达到 -- 创建新的连接
2、达到
1、判断之前最早那个正在做任务的连接对象是否有超时
1、有超时,将之前未做完的动作全部回滚,并且基于之前那个连接对象的真实Connection创建一个新的PooledConnection返回
2、没有超时,而且也没有空闲的,而且也有达到最大连接数,只能等待(计时等待)
*/
if (state.activeConnections.size() < poolMaximumActiveConnections) {
//判断没有达到最大连接数,创建
conn = new PooledConnection(dataSource.getConnection(), this);
if (log.isDebugEnabled()) {
log.debug("Created connection " + conn.getRealHashCode() + ".");
}
} else {
//已经达到最大连接数
//获取最早的那个正在执行任务的连接对象
PooledConnection oldestActiveConnection = state.activeConnections.get(0);
//获取执行的时长
long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
//比较执行时长和最大超时时间
if (longestCheckoutTime > poolMaximumCheckoutTime) {
//超时
state.claimedOverdueConnectionCount++;
state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
state.accumulatedCheckoutTime += longestCheckoutTime;
state.activeConnections.remove(oldestActiveConnection);
if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
try {
//回滚,将之前没有执行完的任务回复到最初状态 oldestActiveConnection.getRealConnection().rollback();
} catch (SQLException e) {
/*
Just log a message for debug and continue to execute the following
statement like nothing happened.
Wrap the bad connection with a new PooledConnection, this will help
to not interrupt current executing thread and give current thread a
chance to join the next competition for another valid/good database
connection. At the end of this loop, bad {@link @conn} will be set as null.
*/
log.debug("Bad connection. Could not roll back");
}
}
//基于最早那个连接的真实连接对象,构建PooledConnection
conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());
conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());
oldestActiveConnection.invalidate();
if (log.isDebugEnabled()) {
log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
}
} else {
//没有超时,只能等待
try {
if (!countedWait) {
state.hadToWaitCount++;
countedWait = true;
}
if (log.isDebugEnabled()) {
log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
}
long wt = System.currentTimeMillis();
//等待,等待poolTimeToWait
state.wait(poolTimeToWait);
state.accumulatedWaitTime += System.currentTimeMillis() - wt;
} catch (InterruptedException e) {
//等待超时,直接结束while循环
break;
}
}
}
}
if (conn != null) {
// ping to server and check the connection is valid or not
if (conn.isValid()) {
if (!conn.getRealConnection().getAutoCommit()) {
conn.getRealConnection().rollback();
}
conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));
conn.setCheckoutTimestamp(System.currentTimeMillis());
conn.setLastUsedTimestamp(System.currentTimeMillis());
state.activeConnections.add(conn);
state.requestCount++;
state.accumulatedRequestTime += System.currentTimeMillis() - t;
} else {
if (log.isDebugEnabled()) {
log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection.");
}
state.badConnectionCount++;
localBadConnectionCount++;
conn = null;
if (localBadConnectionCount > (poolMaximumIdleConnections + poolMaximumLocalBadConnectionTolerance)) {
if (log.isDebugEnabled()) {
log.debug("PooledDataSource: Could not get a good connection to the database.");
}
throw new SQLException("PooledDataSource: Could not get a good connection to the database.");
}
}
}
}
}
if (conn == null) {
if (log.isDebugEnabled()) {
log.debug("PooledDataSource: Unknown severe error condition. The connection pool returned a null connection.");
}
throw new SQLException("PooledDataSource: Unknown severe error condition. The connection pool returned a null connection.");
}
return conn;
}
- PooledConnection
//调用PooledConnection的所有方法,实际执行的是invoke
class PooledConnection implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
//如果调用的是close方法,最终执行的是dataSource.pushConnection(this); -- 归还连接
if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) {
dataSource.pushConnection(this);
return null;
}
try {
if (!Object.class.equals(method.getDeclaringClass())) {
// issue #579 toString() should never fail
// throw an SQLException instead of a Runtime
checkConnection();
}
//如果调用的是其他方法, 调用原生Connection对象的方法
return method.invoke(realConnection, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
}
- 连接的归还
protected void pushConnection(PooledConnection conn) throws SQLException {
synchronized (state) {
//将归还的连接从正在执行任务的容器中移除
state.activeConnections.remove(conn);
if (conn.isValid()) {
if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {
state.accumulatedCheckoutTime += conn.getCheckoutTime();
//如果连接不是自动提交,而且我们也没有去调用commit方法提交
//这些数据是无效数据,会自动帮助回滚
if (!conn.getRealConnection().getAutoCommit()) {
conn.getRealConnection().rollback();
}
//基于真实的连接对象,重新new了一个PooledConnectoin对象
PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);
//将其添加到空闲的容器中
state.idleConnections.add(newConn);
newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
//销毁原来那一个
conn.invalidate();
if (log.isDebugEnabled()) {
log.debug("Returned connection " + newConn.getRealHashCode() + " to pool.");
}
//将之前正在获取链接,而没有获取到并进入等待的线程,唤醒
state.notifyAll();
} else {
state.accumulatedCheckoutTime += conn.getCheckoutTime();
if (!conn.getRealConnection().getAutoCommit()) {
conn.getRealConnection().rollback();
}
conn.getRealConnection().close();
if (log.isDebugEnabled()) {
log.debug("Closed connection " + conn.getRealHashCode() + ".");
}
conn.invalidate();
}
} else {
if (log.isDebugEnabled()) {
log.debug("A bad connection (" + conn.getRealHashCode() + ") attempted to return to the pool, discarding connection.");
}
state.badConnectionCount++;
}
}
}
3、Interceptor实现增强的原理解析:
3.1、增强代理对象的生成
//DefaultSqlSessionFactory
@Override
public SqlSession openSession(boolean autoCommit) {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
}
//DefaultSqlSessionFactory
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//创建一个Executor对象
final Executor executor = configuration.newExecutor(tx, execType);
//我们获取到的SqlSession对象是DefaultSqlSession类型的
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();
}
}
//Configuration
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
//默认是创建的这个Executor
executor = new SimpleExecutor(this, transaction);
}
//mybatis默认是支持缓存的,会在原本的SimpleExecutor基础上再包装一层
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
//利用Interceptor,生成代理对象,从而对Executor中的某些方法实现增强
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
//InterceptorChain
public Object pluginAll(Object target) {
//拿到之前配置的所有的Interceptor,依次生成代理对象
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
//Interceptor
default Object plugin(Object target) {
//返回一个代理对象
return Plugin.wrap(target, this);
}
//Plugin类
public class Plugin implements InvocationHandler {
public static Object wrap(Object target, Interceptor interceptor) {
//解析Interceptor的@Interceptors注解
//获取要拦截的类和方法信息
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
//利用jdk的代理技术,返回一个动态代理对象
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
//这个Plugin实际是InvocationHandler对象
//在这个对象中,保存了我们的Executor对象
//还保存了Interceptor对象
//含保存了当前这个Interceptor对象需要拦截的类和方法
//所以,调用Executor的所有方法,会执行Plugin的invoke方法
new Plugin(target, interceptor, signatureMap));
}
return target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//获取当前调用方法所对应的类中的拦截的方法
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
//判断当前调用的方法是否需要拦截
if (methods != null && methods.contains(method)) {
//需要拦截,调用Interceptor的intercept方法
return interceptor.intercept(new Invocation(target, method, args));
}
//如果不需要拦截,原封不动调用
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
}
3.2、增强代理对象的调用
Inteceptor增强的目标对象是MyBatis中的四大组件
四大组件:Executor,StatementHandler,ParameterHandler,ResultSetHandler
我们需要知道这个强逻辑是怎么触发的,只需要找到这四个对象调用方法的代码即可
- 获取Mapper的代理对象
//MapperProxyFactory
public T newInstance(SqlSession sqlSession) {
//这个对象是一个InvocationHandler对象
//我们调用Mapper代理对象的所有方法,都会执行这个对象的invoke方法
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
- MapperProxy的invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//Object类中的方法调用,不管
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
//是接口中的默认方法调用,也不管
} else if (method.isDefault()) {
if (privateLookupInMethod == null) {
return invokeDefaultMethodJava8(proxy, method, args);
} else {
return invokeDefaultMethodJava9(proxy, method, args);
}
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
//这个才是我们mapper中的接口方法调用时,执行的具体代码
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
//MapperMethod类中的execute方法
//根据不同的操作,最终调用SqlSessoion中的insert、update、delete、selectList、selectOne等方法
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
分析到这,也就是说我们调用mapper接口对象的增删改查操作,最终都是调用了SqlSession的增删改改查方法;换句话讲,咱们要去看Executor的update/query方法的具体调用位置,还是要去看SqlSession的增删改改查方法
- 这里选择SqlSession的selectList方法查看
//DefaultSqlSession
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
//如果之前配置了插件,那么这个地方拿到的executor其实是一个代理对象
//调用这个对象的所有方法,都会触发代理对象的invoke方法执行
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
//CachingExecutor
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
//构建缓存的key
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
//CachingExecutor
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
//查看是否有缓存对象(二级缓存)
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
//没有二级缓存,执行该操作
//调用底层的Executor对象去进行查询
//调用的是BaseExecutor
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
//BaseExecutor
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
//这里是从一级缓存中获取结果
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
//一级缓存中有结果,不去查询数据库
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//一级缓存中没有数据,去底层数据库中查询
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
//从底层数据库查询数据的方法
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
//先给这个key绑定一个假的缓存对象(缓存的前置处理)
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
//真正执行数据库查询操作的方法
//执行的是SimpleExecutor的doQuery方法
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
//干掉之前存的假缓存对象
localCache.removeObject(key);
}
//保存一个真的缓存对象
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
//SimpleExecutor
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
//构建四大组件之一的StatementHandler对象
//在这个方法内部,有一段Inteceptor的增强处理(动态代理)
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
//Configuration
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//创建目标对象 StatementHandler
//在创建这个对象是,会在这个对象的内部构建ParameterHandler和ResultSetHandler对象,并且会利用Interceptor对这两个对象实现代理增强逻辑
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
//利用Inteceptor实现增强操作
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
//StatementHandler
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) {
//这个是Statement类型 (对应mysql的Statement对象)
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
//这个是PreparedStatement类型(对应mysql的PreparedStatement对象)
//我们这里构建的是这个对象
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
//这个是执行数据库存储过程的类型(对应mysql的CallableStatement对象)
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
//PreparedStatementHandler
public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
}
//super
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
this.configuration = mappedStatement.getConfiguration();
this.executor = executor;
this.mappedStatement = mappedStatement;
this.rowBounds = rowBounds;
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.objectFactory = configuration.getObjectFactory();
if (boundSql == null) { // issue #435, get the key before calculating the statement
generateKeys(parameterObject);
boundSql = mappedStatement.getBoundSql(parameterObject);
}
this.boundSql = boundSql;
//构建ParameterHandler,内部会使用Interceptor实现增强
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
//构建ResultSetHandler,内部会使用Interceptor实现增强
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
//Configuration
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
//使用Interceptor实现增强
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) {
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
//使用Interceptor实现增强
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
4、缓存实现逻辑的分析
一级缓存是基于SqlSession对象的,同一个事务中,前后两次执行相同的查询操作,第二次就不会去查询数据库了,而是直接从缓存中获取;如果SqlSession对象执行了close操作,那么一级缓存被清空
二级缓存是基于namespace,只要是同一个namespace范围,即使是不同的SqlSession事务对象,前后两次执行相同的查询操作,第二次就不会去查询数据库了,而是直接从二级缓存中获取
一级缓存是原生支持的,底层是通过一个HashMap来存储数据
二级缓存是需要配置的,底层通过LruCache完成数据存储,LruCache底层使用LinkedHashMap实现
如果既有一级缓存,又有二级缓存,那么优先走二级缓存
4.1、测试一级缓存
- 一级缓存有效
- 一级缓存失效
4.2、测试二级缓存
4.2.1、添加配置,开启二级缓存
4.2.2、测试二级缓存
4.3、缓存的源码分析
//DefaultSqlSession
@Override
public <E> List<E> selectList(String statement) {
return this.selectList(statement, null);
}
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
//这里调用CachingExecutor
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
//CacheExecutor
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
//获取二级缓存的缓存对象(LruCache类型)
//这个对象能获取的前提是开启二级缓存
Cache cache = ms.getCache();
if (cache != null) {
//获取到了二级缓存对象
flushCacheIfRequired(ms);
//是否要缓存(默认是true)&& resultHandler是否为空(这个值前期都是null)
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
//去二级缓存对象中获取缓存
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
//如果二级缓存为null,去查询,执行的是BaseExecutor的query方法
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
//将结果存入二级缓存对象
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
//二级缓存对象为null,直接去查询,执行的是BaseExecutor的query方法
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
//BaseExecutor
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
//通过localCache去查询key对应的缓存结果
//localCache底层其实是一个HashMap
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
//缓存不为null,这里去处理存储过程的出参结果
//不是一个存储过程,所以不需要处理
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//如果一级缓存是null,从底层数据库查询
//并且查询到后,会存入localCache中
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
//从底层数据库查询的方法
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
//给当前这个key占个位
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
//真正去数据库查询
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
//先把之前占位的对象干掉
localCache.removeObject(key);
}
//把真正的结果数据存入localCache中
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
到此我们看到了一级缓存和二级缓存的过程
一级缓存是原生存在的,不再分析
二级缓存的Cache对象到底是怎么产生的,接下来我们一起来分析
Cache cache = ms.getCache(); //ms 是 MappedStatement类型(接口中的每一个增删改查方法,都会被封装成一个MappedStatement对象)
//XMLConfigBuilder
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
//解析mapper映射文件/mapper接口
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
//解析核心配置文件的mappers节点
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
//配置的是package
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
//将指定包下的所有.class文件,加载进内存,形成Class对象
//并且会对每一个Class对象的内容做解析
//把类中的所有方法都变成MapperedStatement对象
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
//如果配置的mapper的resource
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
//如果配置的mapper的url
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
//如果配置的mapper的class
} else if (resource == null && url == null && mapperClass != null) {
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
//MapperRegistry
public void addMappers(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
//查找包下的所有class文件,并且加载到内存,形成Class对象
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
//遍历到每一个class对象,将内部的方法都解析成M阿婆peredStatement对象
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<>(type));
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
//解析类中的所有方法
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
//MapperedAnnotationBuilder
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
loadXmlResource();
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
//解析CacheNamespace注解,如果存在,会构建一个Cache对象
//并且将这个构建出来的Cache对象赋值给一个currentCache变量
parseCache();
parseCacheRef();
Method[] methods = type.getMethods();
//将接口中的每一个方法,都变成一个MapperedStatement对象, 并且将之前currentCache所指向的缓存对象,赋值给MapperedStatement中的cache属性
for (Method method : methods) {
try {
// issue #237
if (!method.isBridge()) {
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
parsePendingMethods();
}