• 2. Executor与SqlSession详解



    在使用mybatis时,一般都是书写mapper文件来执行sql,mapper文件中sql下发到数据库执行要经过下面几个步骤:

    在这里插入图片描述

    1. MapperProxy接口代理: 用于代理mapper文件中sql执行接口,其目的是简化对MyBatis使用,底层使用动态代理实现;
    2. SqlSession:sql会话,提供增删改查API,其本身不作任何业务逻辑的处理,所有处理都交给执行器。这是一个典型的门面模式设计
    3. Executor:处理器,核心作用是处理SQL请求、事物管理、维护缓存以及批处理等 。执行器在的角色更像是一个管理员,接收SQL请求,然后根据缓存、批处理等逻辑来决定如何执行这个SQL请求。并交给JDBC处理器执行具体SQL。
    4. StatementHandler:JDBC处理器,他的作用就是用于通过JDBC具体处理SQL和参数的。在会话中每调用一次CRUD,JDBC处理器就会生成一个实例与之对应(命中缓存除外)。

    下面我们集中介绍下Executor

    1. Executor功能介绍

    Executor是mybatis的执行接口,执行器主要包括如下功能:

    • 基本功能:改、查,没有增删的原因是,所有的增删操作都可以归结到改;
    • 缓存维护:这里的缓存主要是为一级缓存服务,功能包括创建缓存Key、清理缓存、判断缓存是否存在;
    • 事物管理:提交、回滚、关闭、批处理刷新。

    下面看下执行器的上下继承关系
    在这里插入图片描述
    共包括3中Executor执行器,分别为SimpleExecutor、ReuseExecutor、BatchExecutor,其中BaseExecutor是封装了获取链接、维护事物、以及一级缓存相关的通用操作,三种执行器都是继承BaseExecutor而来。

    2. Executor执行器种类

    2.1 SimpleExecutor简单执行器

    SimpleExecutor是默认执行器,它的行为是每处理一次会话当中的SQl请求都会通过对应的StatementHandler 构建一个新个Statement,这就会导致即使是相同SQL语句也无法重用Statement。
    案例如下所示,本篇所有案例环境构建参考此篇文章

    package com.lzj.example.executor;
    
    import com.lzj.example.MybatisUtil;
    import org.apache.ibatis.executor.SimpleExecutor;
    import org.apache.ibatis.mapping.MappedStatement;
    import org.apache.ibatis.session.Configuration;
    import org.apache.ibatis.session.RowBounds;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import java.sql.SQLException;
    import java.util.List;
    
    @Component
    public class SimpleExecutorExample {
    
        @Autowired
        private MybatisUtil mybatisUtil;
    
        public void SimpleExecutorTest() throws SQLException {
            Configuration configuration = mybatisUtil.getConfiguration();
            MappedStatement ms = configuration.getMappedStatement("com.lzj.dao.UserDao.selectOne");
            SimpleExecutor executor = new SimpleExecutor(mybatisUtil.getConfiguration(), mybatisUtil.getJdbcTransaction());
            List<Object> lists = executor.doQuery(ms, 1, RowBounds.DEFAULT, SimpleExecutor.NO_RESULT_HANDLER, ms.getBoundSql(1));
            System.out.println(lists.get(0));
            List<Object> lists1 = executor.doQuery(ms, 1, RowBounds.DEFAULT, SimpleExecutor.NO_RESULT_HANDLER, ms.getBoundSql(1));
            System.out.println(lists1.get(0));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    执行该案例会有如下输出,可见对于同样的一条SQL,简单执行器也是要编译两遍的。

    ……
    2022-11-12 23:07:21.515 DEBUG 12392 --- [           main] com.lzj.dao.UserDao.selectOne            : ==>  Preparing: select * from user where id=? 
    2022-11-12 23:07:21.562 DEBUG 12392 --- [           main] com.lzj.dao.UserDao.selectOne            : ==> Parameters: 1(Integer)
    2022-11-12 23:07:21.620 DEBUG 12392 --- [           main] com.lzj.dao.UserDao.selectOne            : <==      Total: 1
    User{id=1, name='xiaowang', age=22}
    2022-11-12 23:07:21.624 DEBUG 12392 --- [           main] com.lzj.dao.UserDao.selectOne            : ==>  Preparing: select * from user where id=? 
    2022-11-12 23:07:21.624 DEBUG 12392 --- [           main] com.lzj.dao.UserDao.selectOne            : ==> Parameters: 1(Integer)
    2022-11-12 23:07:21.625 DEBUG 12392 --- [           main] com.lzj.dao.UserDao.selectOne            : <==      Total: 1
    User{id=1, name='xiaowang', age=22}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.2 ReuseExecutor可重用执行器

    ReuseExecutor 区别在于他会将在会话期间内的Statement进行缓存,并使用SQL语句作为Key。所以当执行下一请求的时候,不在重复构建Statement,而是从缓存中取出并设置参数,然后执行。

    package com.lzj.example.executor;
    
    import com.lzj.example.MybatisUtil;
    import org.apache.ibatis.executor.ReuseExecutor;
    import org.apache.ibatis.executor.SimpleExecutor;
    import org.apache.ibatis.mapping.MappedStatement;
    import org.apache.ibatis.session.Configuration;
    import org.apache.ibatis.session.RowBounds;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import java.sql.SQLException;
    import java.util.List;
    
    @Component
    public class ReuseExecutorExample {
    
        @Autowired
        private MybatisUtil mybatisUtil;
    
        public void resueExecutorTest() throws SQLException {
            Configuration configuration = mybatisUtil.getConfiguration();
            MappedStatement ms = configuration.getMappedStatement("com.lzj.dao.UserDao.selectOne");
            ReuseExecutor executor = new ReuseExecutor(configuration, mybatisUtil.getJdbcTransaction());
            List<Object> lists = executor.doQuery(ms, 1, RowBounds.DEFAULT, SimpleExecutor.NO_RESULT_HANDLER, ms.getBoundSql(1));
            System.out.println(lists.get(0));
            List<Object> lists1 = executor.doQuery(ms, 2, RowBounds.DEFAULT, SimpleExecutor.NO_RESULT_HANDLER, ms.getBoundSql(2));
            System.out.println(lists1.get(0));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    执行该案例,输出包含如下所示,从日志可以看出对于可重用执行器执行同条SQL只是参数不同,只进行一次编译,每次设置不同参数而已。

    ……
    2022-11-12 23:20:05.059 DEBUG 15456 --- [           main] com.lzj.dao.UserDao.selectOne            : ==>  Preparing: select * from user where id=? 
    2022-11-12 23:20:05.099 DEBUG 15456 --- [           main] com.lzj.dao.UserDao.selectOne            : ==> Parameters: 1(Integer)
    2022-11-12 23:20:05.152 DEBUG 15456 --- [           main] com.lzj.dao.UserDao.selectOne            : <==      Total: 1
    User{id=1, name='xiaowang', age=22}
    2022-11-12 23:20:05.154 DEBUG 15456 --- [           main] com.lzj.dao.UserDao.selectOne            : ==> Parameters: 2(Integer)
    2022-11-12 23:20:05.157 DEBUG 15456 --- [           main] com.lzj.dao.UserDao.selectOne            : <==      Total: 1
    User{id=2, name='xiaoli', age=25}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    :执行器都不能跨线程调用。

    2.3 BatchExecutor批处理执行器

    BatchExecutor 顾名思议,它就是用来作批处理的。但会将所 有SQL请求集中起来,最后调用Executor.flushStatements() 方法时一次性将所有请求发送至数据库。
    BatchExecutor只对增删改的SQL才有效。该执行器将SQL一次性插入数据库是有条件的,即只有连续相同的SQL语句并且相同的SQL才会重用Statement,并利用其批处理功能,否则就不能利用批处理功能。
    看下面案例

    package com.lzj.example.executor;
    
    import com.lzj.bean.User;
    import com.lzj.example.MybatisUtil;
    import org.apache.ibatis.executor.BatchExecutor;
    import org.apache.ibatis.mapping.MappedStatement;
    import org.apache.ibatis.session.Configuration;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import java.sql.SQLException;
    import java.util.HashMap;
    import java.util.Map;
    
    @Component
    public class BatchExecutorExample {
        @Autowired
        private MybatisUtil mybatisUtil;
    
        public void batchExecutorTest() throws SQLException {
            Configuration configuration = mybatisUtil.getConfiguration();
            MappedStatement ms = configuration.getMappedStatement("com.lzj.dao.UserDao.updAge");
            MappedStatement ms1 = configuration.getMappedStatement("com.lzj.dao.UserDao.insert");
            BatchExecutor executor = new BatchExecutor(configuration, mybatisUtil.getJdbcTransaction());
            User user = new User();
            user.setId(1);
            user.setAge(23);
            executor.doUpdate(ms, user);
            user.setId(3);
            user.setName("xiaozhang");
            user.setAge(30);
            executor.doUpdate(ms1, user);
            user.setId(2);
            user.setAge(26);
            executor.doUpdate(ms, user);
            executor.doFlushStatements(false);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    执行案例输出如下所示,虽然两条update语句映射相同,但两条update中间增加了一条insert语句,此时就会导致不能利用批处理功能。如果去掉insert语句,那么这两条update语句是可以利用批处理一次性更新库表的。

    2022-11-12 23:56:11.771 DEBUG 14700 --- [           main] com.lzj.dao.UserDao.updAge               : ==>  Preparing: update user set age=? where id=? 
    2022-11-12 23:56:11.817 DEBUG 14700 --- [           main] com.lzj.dao.UserDao.updAge               : ==> Parameters: 23(Integer), 1(Integer)
    2022-11-12 23:56:11.817 DEBUG 14700 --- [           main] com.lzj.dao.UserDao.insert               : ==>  Preparing: insert into user values (?,?,?) 
    2022-11-12 23:56:11.817 DEBUG 14700 --- [           main] com.lzj.dao.UserDao.insert               : ==> Parameters: 3(Integer), xiaozhang(String), 30(Integer)
    2022-11-12 23:56:11.818 DEBUG 14700 --- [           main] com.lzj.dao.UserDao.updAge               : ==>  Preparing: update user set age=? where id=? 
    2022-11-12 23:56:11.820 DEBUG 14700 --- [           main] com.lzj.dao.UserDao.updAge               : ==> Parameters: 26(Integer), 2(Integer)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.4 CachingExecutor二级缓存执行器

    除了上面演示的从BaseExecutor继承而来的3中执行器,还有一种是直接实现Exector接口而来的CachingExecutor二级缓存执行器(后面会讲二级缓存与一级缓存的区别)。
    在这里插入图片描述
    为什么一级缓存的功能在BaseExecutor,二级缓存确单独实现了个CachingExecutor?因为一级缓存天生就是存在的,二级缓存是设置了才会有,不设置就没有,如果把二级缓存也设置到BaseExecutor中就会增加BaseExecutor中分支增加,职责也不专一。
    CachingExecutor利用装饰器模式对SimpleExecutor或者ReuseExecutor或者BatchExecutor进行装饰,在构造CachingExecutor时需要传一个Executor,也即三种执行器中任何一种都可以,CachingExecutor只执行二级缓存的逻辑,其余操作比如查询、修改逻辑还是交给包装的Executor的执行器进行执行。
    CachingExecutor执行SQL必须commit后,后续如有相同SQL执行才会命中缓存,因为二级缓存是可以跨线程处理的,如果在一个session中没有进行commit处理两条相同的SQL那是命中的一级缓存。
    如需二级缓存首先要开启二级缓存,比如在application.yml中添加mybatis.configuration.cache-enabled: true

    mybatis:
      mapper-locations: classpath*:mapper/*Mapper.xml
      configuration:
        cache-enabled: true
    
    • 1
    • 2
    • 3
    • 4

    然后在需要二级缓存的mapper中添加配置

    
    DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    
    <mapper namespace="com.lzj.dao.UserDao">
        <cache>cache>
        
        
        <select id="selectOne" parameterType="int" resultType="com.lzj.bean.User">
            select * from user where id=#{id}
        select>
    
        <update id="updAge" parameterType="com.lzj.bean.User">
            update user set age=#{age} where id=#{id}
        update>
    
        <insert id="insert" parameterType="com.lzj.bean.User">
            insert into user values (#{id},#{name},#{age})
        insert>
    mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    下面看如下测试案例

    package com.lzj.example.executor;
    
    import com.lzj.example.MybatisUtil;
    import org.apache.ibatis.executor.CachingExecutor;
    import org.apache.ibatis.executor.SimpleExecutor;
    import org.apache.ibatis.mapping.MappedStatement;
    import org.apache.ibatis.session.Configuration;
    import org.apache.ibatis.session.RowBounds;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import java.sql.SQLException;
    import java.util.List;
    
    @Component
    public class CachingExecutorExample {
    
        @Autowired
        private MybatisUtil mybatisUtil;
    
        public void cachingExecutorTest() throws SQLException {
            Configuration configuration = mybatisUtil.getConfiguration();
            MappedStatement ms = configuration.getMappedStatement("com.lzj.dao.UserDao.selectOne");
            SimpleExecutor executor = new SimpleExecutor(mybatisUtil.getConfiguration(), mybatisUtil.getJdbcTransaction());
            CachingExecutor cachingExecutor = new CachingExecutor(executor);
            cachingExecutor.query(ms, 1, RowBounds.DEFAULT, SimpleExecutor.NO_RESULT_HANDLER);
            cachingExecutor.query(ms, 1, RowBounds.DEFAULT, SimpleExecutor.NO_RESULT_HANDLER);
            cachingExecutor.commit(true);
            cachingExecutor.query(ms, 1, RowBounds.DEFAULT, SimpleExecutor.NO_RESULT_HANDLER);
            cachingExecutor.query(ms, 1, RowBounds.DEFAULT, SimpleExecutor.NO_RESULT_HANDLER);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    运行该案例有如下输出,从日志可以看出,第一次select时缓存命中率为0.0,这个很好理解,第一次查询还没有缓存所以命中率为0.0;而第二次命中率也是0.0,为什么呢?因为第一次和第二次之间没有commit,导致第二次查询命中的是一级缓存而不是二级缓存;第二次查询后进行了commit,可以看出后面两次查询二级缓存命中率逐渐增高。

    2022-11-13 15:33:22.903 DEBUG 17948 --- [nio-8004-exec-2] com.lzj.dao.UserDao                      : Cache Hit Ratio [com.lzj.dao.UserDao]: 0.0
    2022-11-13 15:33:22.929 DEBUG 17948 --- [nio-8004-exec-2] com.lzj.dao.UserDao.selectOne            : ==>  Preparing: select * from user where id=? 
    2022-11-13 15:33:22.993 DEBUG 17948 --- [nio-8004-exec-2] com.lzj.dao.UserDao.selectOne            : ==> Parameters: 1(Integer)
    2022-11-13 15:33:23.055 DEBUG 17948 --- [nio-8004-exec-2] com.lzj.dao.UserDao.selectOne            : <==      Total: 1
    2022-11-13 15:33:23.062 DEBUG 17948 --- [nio-8004-exec-2] com.lzj.dao.UserDao                      : Cache Hit Ratio [com.lzj.dao.UserDao]: 0.0
    2022-11-13 15:33:23.066 DEBUG 17948 --- [nio-8004-exec-2] com.lzj.dao.UserDao                      : Cache Hit Ratio [com.lzj.dao.UserDao]: 0.3333333333333333
    2022-11-13 15:33:23.067 DEBUG 17948 --- [nio-8004-exec-2] com.lzj.dao.UserDao                      : Cache Hit Ratio [com.lzj.dao.UserDao]: 0.5
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3 SqlSession

    SqlSession是mybatis中sql的会话,通过SqlSession可以执行SQL命令、管理事务。SqlSession内部对mybatis的配置以及上述介绍的Executor进行了封装,我们通过SqlSession的一个实现DefaultSqlSession就可以看出,从下面构造器可以看出封装的内容。

      public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
        this.configuration = configuration;
        this.executor = executor;
        this.dirty = false;
        this.autoCommit = autoCommit;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    既然SqlSession进行了封装,因此大大简化了执行SQL的代码,比如执行一个查询操作所示,直接通过SqlSession的selectList一行代码执行数据库获取数据库获取执行结果。

    package com.lzj.example.sqlsession;
    
    import com.lzj.example.MybatisUtil;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import java.util.List;
    
    @Component
    public class SqlSessionExample {
    
        @Autowired
        private MybatisUtil mybatisUtil;
    
        public void sqlSessionTest1(){
            SqlSessionFactory factory = mybatisUtil.getFactory();
            SqlSession sqlSession = factory.openSession(true);  //true表示自动提交
            List<Object> list = sqlSession.selectList("com.lzj.dao.UserDao.selectOne", 2);
            System.out.println(list.get(0));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    MybatisUtil中获取factory的代码如下所示

    SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
    factory = factoryBuilder.build(MybatisUtil.class.getResourceAsStream("/mybatis-config.xml"));
    
    • 1
    • 2

    mybatis-config.xml的配置如下所示

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <settings>
            <!--开启二级缓存-->
            <setting name="cacheEnabled" value="true"/>
        </settings>
    
        <!--数据库配置-->
        <environments default="mysql">
            <environment id="mysql">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/lzj?characterEncoding=utf8"/>
                    <property name="username" value="root"/>
                </dataSource>
            </environment>
        </environments>
    
        <!--mapper位置-->
        <mappers>
            <mapper resource="mapper/UserMapper.xml"></mapper>
        </mappers>
    </configuration>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    执行List list = sqlSession.selectList("com.lzj.dao.UserDao.selectOne", 2);一句时,mybatis底层其实是按如图所示流程进行执行的,先通过SqlSession获取了CachingExecutor二级缓存执行器,如果二级缓存中已缓存SQL直接返回,如果未命中缓存则获取CachingExecutor包装的BaseExecutor执行器(本案例中是SimpleExecutor执行器),然后判断BaseExecutor执行器中是否命中一级缓存,如果命中则直接返回,否则继续调SimpleExecutor中的query方法查询数据库。
    在这里插入图片描述
    通过打断点的方式可以看出默认的DefaultSqlSession内部包装的CachingExecutor二级缓存执行器,二级缓存执行器内部装饰了SimpleExecutor简单执行器。
    在这里插入图片描述
    下面我们通过跟踪源码方式一步步剖析执行流程。
    首先执行了DefaultSqlSession中selectList方法,根本就是调用二级缓存先查询缓存数据。

      @Override
      public <E> List<E> selectList(String statement, Object parameter) {
        return this.selectList(statement, parameter, RowBounds.DEFAULT);
      }
    
      @Override
      public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        try {
    	  /*首先通过statement id获取mapper接口对应的MappedStatement, statement id就代表mapper文件中方法id,一个mapper文件不能有重复的id*/
          MappedStatement ms = configuration.getMappedStatement(statement);
    	  /*executor代表二级二级换成执行器,首先调用二级查询方法查询sql*/
          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();
        }
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    MappedStatement解释:每个MappedStatement对应了我们自定义Mapper接口中的一个方法,它保存了开发人员编写的SQL语句、参数结构、返回值结构、Mybatis对它的处理方式的配置等细节要素,是对一个SQL命令是什么、执行方式的完整定义。可以说,有了它Mybatis就知道如何去调度四大组件顺利的完成用户请求。

    下面调二级缓存执行器的query方法来获取缓存中数据或者从数据库中获取数据。

      @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);
      }
      
        @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) {
    	  /*检查mapper文件中,是否有要求刷新缓存*/
          flushCacheIfRequired(ms);
    	  /*判断mapper文件中是否运行二级缓存;并且对于缓存的sql,不再运行自定义ResultHandler*/
          if (ms.isUseCache() && resultHandler == null) {
    		/*mapper文件中书写的SQL确保输入参数是in模式*/
            ensureNoOutParams(ms, boundSql);
            @SuppressWarnings("unchecked")
    		/*从缓存中获取结果*/
            List<E> list = (List) tcm.getObject(cache, key);
            if (list == null) {
    		  /*如果缓存中无结果,从二级缓存执行器装饰的执行器也即SimpleExecutor执行器查询数据库获取结果*/
              list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    		  /*获取的结果放到缓存中*/
              tcm.putObject(cache, key, list); // issue #578 and #116
            }
            return list;
          }
        }
    	/*如果缓存为空,就调用二级缓存装饰的执行器,这个地方为SimpleExecutor进行查询SQL*/
        return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    下一篇文章分析一级缓存和二级缓存源码

    参考:源码阅读网
    
    
    
    • 1
    • 2
    • 3
  • 相关阅读:
    javaee实验,Spring MVC拦截器的设计与实现
    SpringBoot 学习(九)Redis
    链表
    ESP8266-Arduino编程实例-L9110直流电机驱动
    [附源码]计算机毕业设计JAVA超市收银系统论文
    聊一聊对一个 C# 商业程序的反反调试
    猿创征文|【Vue3】插槽(Slot)基础使用
    web安全学习笔记(12)
    Github每日精选(第41期):跨端数据同步加密工具restic
    【Python21天学习挑战赛】集合 & 数据类型补充
  • 原文地址:https://blog.csdn.net/u010502101/article/details/127685362