Mybaties高级 注解开发 延迟加载 SQL构建 Mybatis源码分析

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使用懒加载的条件

多表联查时不能实现懒加载,或者说根本不涉及

  1. 存在多表查询
  2. 不能使用多表联查
  3. 被关联的对象不会被立即使用

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、测试一级缓存

  • 一级缓存有效

在这里插入图片描述
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-udBO8tLs-1594565633888)(img/微信截图_20200701195007.png)]

  • 一级缓存失效

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-saWLlQXc-1594565633889)(img/微信截图_20200701195134.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M8hikAqQ-1594565633891)(img/微信截图_20200701195251.png)]

4.2、测试二级缓存

4.2.1、添加配置,开启二级缓存

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TuS20Udp-1594565633892)(img/微信截图_20200701195550.png)]

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();
}

总结

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值