mybatis源码---Executor执行器

2 篇文章 0 订阅
2 篇文章 0 订阅

java原生jdbc操作

流程

  1. 读取配置信息(基本信息)
  2. 加载驱动
  3. 获取连接
  4. 获取PreparedStatement的实例 (或:预编译sql语句)
  5. 针对结果集的处理(增删改用 execute() 不需处理结果集,查询用 .executeQuery(); )
  6. 关闭连接

流程图

jdbc执行流程

示例

//获取连接
Connection conn = JDBCUtils.getConnection(); 
String sql = "insert intocustomers(name,email,birth,photo)values(?,?,?,?)";
//调用 PreparedStatement进行预编译
PreparedStatement ps = conn.prepareStatement(sql); 
// 填充占位符 
ps.setString(1, "徐海强");
ps.setString(2, "xhq@126.com");
ps.setDate(3, new Date(new java.util.Date().getTime())); 
// 操作Blob类型的变量 
FileInputStream fis = new FileInputStream("xhq.png"); 
ps.setBlob(4, fis);
//执行 
ps.execute();
fis.close();
JDBCUtils.closeResource(conn, ps);

java原生的执行器

在这里插入图片描述

1.Statement可以支持重用执行多个静态SQL,并可以设置addBatch、setFetchSize等操作。Statement的每次执行都是给数据库发送一个静态SQL。多次执行,即发送多个静态SQL。
2.PreparedStatement可以对SQL进行预编译,可以有效防止SQL注入(参数转义话在数据端执行,并非在Applicattion)。并且,每次执行都是给数据库发送一个SQL,加上若干组参数。
3.CallableStatement集成以上两个接口的基础上,扩展了返回结果的读写。

mybatis工作流程

Mybatis作为封装JDBC操作的半自动框架,也离不开JDBC的基本流程,以及java.sql给出的规范。

mybatis执行流程

在这里插入图片描述

Mybatis将Connection对象维护交由SqlSession这个环节来处理,将SQL预编译与执行交给Executor这个环节来处理,将结果集提取以及数的处理交给StatemntHandler来处理。(那么执行器扮演者jdbc重要的内容部分)

Executor执行器

结构体系

在这里插入图片描述
1.BaseExecutor:作为Executor的基本抽象实现,里边提取了连接维护、一级缓存的公有功能,供子类复用。

2.CachingExecutor:作为BaseExecutor的一个装饰器,用来负责二级缓存功能。而其他操作都是丢给BaseExecutor来操作。

3.Mybatis的执行器有主要有三种,SimpleExecutor、ReuseExecutor、BatchExecutor。而且默认情况下,Mybatis的三种执行器都底层都调用PreparedStatement。

SimpleExecutor执行器

简单执行每次都会创建一个新的预处理器(PrepareStatement)

源码:

public class SimpleExecutor extends BaseExecutor {

  public SimpleExecutor(Configuration configuration, Transaction transaction) {
    super(configuration, transaction);
  }

  @Override
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
      //初始化statement,调用prepareStatement就会新创建一个statement(默认使用PreparedStatement)
      stmt = prepareStatement(handler, ms.getStatementLog()); 
      //执行,调用对应的PreparedStatement的execute执行
      return handler.update(stmt);
    } finally {
      closeStatement(stmt);
    }
  }

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  //每次执行都会创建一个新Statement 处理
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }

}

前置代码

	//我们采用执行器的方式运行mybatis
    //全局配置
    private Configuration configuration;
    private Transaction transaction;
    private Connection connection;
    @Before
    public void init() throws Exception {
        //加载mybatis文件
        InputStream inputStream = Resources.getResourceAsStream("mybats.xml");
        //加载jdbc配置
        InputStream in = Resources.getResourceAsStream("jdbc.properties");
        Properties properties = new Properties();
        properties.load(in);

        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        configuration=sqlSessionFactory.getConfiguration();
        connection= DriverManager.getConnection(properties.getProperty("jdbc.url"),properties.getProperty("jdbc.username"),properties.getProperty("jdbc.password"));
        transaction=new JdbcTransaction(connection);
    }

测试

 @Test
    public void simpleEXc() throws SQLException {
        //SimpleExecutor简单执行器
        /*
        无论sql是否相同,每次都会进行预编译
         */
        SimpleExecutor executor=new SimpleExecutor(configuration,transaction);
        MappedStatement statement=configuration.getMappedStatement("com.me.dao.UserMapper.selectByPrimaryKey");
        //两条相同的sql
        List<Object> objects = executor.doQuery(statement, 1,
                                                RowBounds.DEFAULT,
                                                SimpleExecutor.NO_RESULT_HANDLER,
                                                statement.getBoundSql(1));
        System.out.println(objects.get(0));
        List<Object> objects2 = executor.doQuery(statement, 1,
                                                RowBounds.DEFAULT,
                                                SimpleExecutor.NO_RESULT_HANDLER,
                                                statement.getBoundSql(1));
        System.out.println(objects2.get(0));
    }

执行

DEBUG 08-10 14:46:48,978 ==>  Preparing: select id, user_name, pass_word, email, td_id from t_user where id = ?  (BaseJdbcLogger.java:137) 
DEBUG 08-10 14:46:49,019 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 08-10 14:46:49,071 <==      Total: 1  (BaseJdbcLogger.java:137) 
User{id=1, userName='cheyuan', passWord='123456', email='dafa@qq.com', tdId=1}

DEBUG 08-10 14:46:49,084 ==>  Preparing: select id, user_name, pass_word, email, td_id from t_user where id = ?  (BaseJdbcLogger.java:137) 
DEBUG 08-10 14:46:49,084 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 08-10 14:46:49,086 <==      Total: 1  (BaseJdbcLogger.java:137) 
User{id=1, userName='cheyuan', passWord='123456', email='dafa@qq.com', tdId=1}
-------------------------------------------------------------------------------------
可以看出两次相同的sql语句,每次都会调用PreparedStatement进行预编译

ReuseExecutor执行器

可重用执行器,底层维护了一个Map<String sql,Statement stmt> 来捕捉到相同的SQL,则直接取对应缓存的Statement进行执行,所以对于相同SQL(包括query、update),不同参数,则只进行一次预编译。

源码

//看prepareStatement方法
public class ReuseExecutor extends BaseExecutor {
	//内部维护了一个HashMap容器存放执行的sql语句
  private final Map<String, Statement> statementMap = new HashMap<>();

  public ReuseExecutor(Configuration configuration, Transaction transaction) {
    super(configuration, transaction);
  }

  @Override
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Configuration configuration = ms.getConfiguration();
    StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
    Statement stmt = prepareStatement(handler, ms.getStatementLog());
    return handler.update(stmt);
  }

  @Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Configuration configuration = ms.getConfiguration();
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
    Statement stmt = prepareStatement(handler, ms.getStatementLog());
    return handler.query(stmt, resultHandler);
  }

  @Override
  protected <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
    Configuration configuration = ms.getConfiguration();
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
    Statement stmt = prepareStatement(handler, ms.getStatementLog());
    return handler.queryCursor(stmt);
  }

  @Override
  public List<BatchResult> doFlushStatements(boolean isRollback) {
    for (Statement stmt : statementMap.values()) {
      closeStatement(stmt);
    }
    statementMap.clear();
    return Collections.emptyList();
  }

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    BoundSql boundSql = handler.getBoundSql();
    String sql = boundSql.getSql();
    //执行器执行前检查是否存在sql
    if (hasStatementFor(sql)) {
      stmt = getStatement(sql);
      applyTransactionTimeout(stmt);
    } else {
    //不存在,则会新建一个Statement存入map
      Connection connection = getConnection(statementLog);
      stmt = handler.prepare(connection, transaction.getTimeout());
      putStatement(sql, stmt);
    }
    handler.parameterize(stmt);
    return stmt;
  }
	//检查map容器中是否存在要执行的sql
  private boolean hasStatementFor(String sql) {
    try {
      Statement statement = statementMap.get(sql);
      return statement != null && !statement.getConnection().isClosed();
    } catch (SQLException e) {
      return false;
    }
  }
	//通过sql得到对应的statement
  private Statement getStatement(String s) {
    return statementMap.get(s);
  }
	//存入一个statement
  private void putStatement(String sql, Statement stmt) {
    statementMap.put(sql, stmt);
  }

}

测试

 @Test
    public void ReuseEXc() throws SQLException {
        //ReuseExecutor 可重用执行器
        /*
            SQL语句相同时,在已经域编译后,不需要在次编译,
         */
        ReuseExecutor executor= new ReuseExecutor(configuration,transaction);
        MappedStatement statement=configuration.getMappedStatement("com.me.dao.UserMapper.selectByPrimaryKey");

        List<Object> objects = executor.doQuery(statement, 1,
                RowBounds.DEFAULT,
                SimpleExecutor.NO_RESULT_HANDLER,
                statement.getBoundSql(1));

        List<Object> objects2 = executor.doQuery(statement, 2,
                RowBounds.DEFAULT,
                SimpleExecutor.NO_RESULT_HANDLER,
                statement.getBoundSql(2));

    }

执行

DEBUG 08-10 15:11:15,432 ==>  Preparing: select id, user_name, pass_word, email, td_id from t_user where id = ?  (BaseJdbcLogger.java:137) 
DEBUG 08-10 15:11:15,471 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 08-10 15:11:15,513 <==      Total: 1  (BaseJdbcLogger.java:137) 
//下面是第二次执行相同的语句,可以看出并没有再次进行预编译,而是直接进行参数处理
DEBUG 08-10 15:11:15,516 ==> Parameters: 2(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 08-10 15:11:15,517 <==      Total: 1  (BaseJdbcLogger.java:137) 

BatchExecutor执行器(是否会重用sql?)

先来测试一下查询

 @Test
    public void BatchEXc() throws SQLException {
        /*BatchExecutor 批处理执行器 简化插入和修改,对于查询,和simple执行器一样,先将所有语句编译好后,再交给数据库进行
            批处理操作必须手动刷新`在这里插入代码片`
         */
        BatchExecutor executor= new BatchExecutor(configuration,transaction);
        MappedStatement statement=configuration.getMappedStatement("com.me.dao.UserMapper.selectByPrimaryKey");

        List<Object> objects = executor.doQuery(statement, 1,
                RowBounds.DEFAULT,
                SimpleExecutor.NO_RESULT_HANDLER,
                statement.getBoundSql(1));

        List<Object> objects2 = executor.doQuery(statement, 2,
                RowBounds.DEFAULT,
                SimpleExecutor.NO_RESULT_HANDLER,
                statement.getBoundSql(2));
        executor.doFlushStatements(false);
    }

执行

DEBUG 08-10 15:16:46,375 ==>  Preparing: select id, user_name, pass_word, email, td_id from t_user where id = ?  (BaseJdbcLogger.java:137) 
DEBUG 08-10 15:16:46,414 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 08-10 15:16:46,451 <==      Total: 1  (BaseJdbcLogger.java:137) 

DEBUG 08-10 15:16:46,453 ==>  Preparing: select id, user_name, pass_word, email, td_id from t_user where id = ?  (BaseJdbcLogger.java:137) 
DEBUG 08-10 15:16:46,453 ==> Parameters: 2(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 08-10 15:16:46,455 <==      Total: 1  (BaseJdbcLogger.java:137) 
可以看出两次相同的sql编译了两次

测试更新

@Test
    public void BatchEXc() throws SQLException {
        /*BatchExecutor 批处理执行器 简化插入和修改,对于查询,和simple执行器一样,先将所有语句编译好后,再交给数据库进行
            批处理操作必须手动刷新才会执行结果
         */
        BatchExecutor executor= new BatchExecutor(configuration,transaction);
        MappedStatement statement=configuration.getMappedStatement("com.me.dao.UserMapper.updateByPrimaryKey");
        int update = executor.doUpdate(statement, new User(6, "dfgtb", "fsdfsf", null, null));
        int update2 = executor.doUpdate(statement, new User(7, "dfhjgtb", "fserfrg", null, null));
        executor.doFlushStatements(false);
    }

执行

DEBUG 08-10 15:25:13,548 ==>  Preparing: update t_user set user_name = ?, pass_word = ?, email = ?, td_id = ? where id = ?  (BaseJdbcLogger.java:137) 
DEBUG 08-10 15:25:13,582 ==> Parameters: dfgtb(String), fsdfsf(String), null, null, 6(Integer)  (BaseJdbcLogger.java:137) 
下一次更新没有再次编译,直接进行参数处理
DEBUG 08-10 15:25:13,583 ==> Parameters: dfhjgtb(String), fserfrg(String), null, null, 7(Integer)  (BaseJdbcLogger.java:137) 

那是不是认为BatchExecutor 也会重用sql呢?

再测试混合情况

   @Test
    public void BatchEXc() throws SQLException {
        /*BatchExecutor 批处理执行器 简化插入和修改,对于查询,和simple执行器一样,先将所有语句编译好后,再交给数据库进行
            批处理操作必须手动刷新才会执行结果
         */
        BatchExecutor executor= new BatchExecutor(configuration,transaction);
        MappedStatement statement=configuration.getMappedStatement("com.me.dao.UserMapper.updateByPrimaryKey");
        int update = executor.doUpdate(statement, new User(6, "dfgtb", "fsdfsf", null, null));

        MappedStatement statement2=configuration.getMappedStatement("com.me.dao.UserMapper.selectByPrimaryKey");

        List<Object> objects = executor.doQuery(statement2, 1,
                RowBounds.DEFAULT,
                SimpleExecutor.NO_RESULT_HANDLER,
                statement2.getBoundSql(1));

        int update2 = executor.doUpdate(statement, new User(7, "dfhjgtb", "fserfrg", null, null));
        executor.doFlushStatements(false);
    }

执行

DEBUG 08-10 15:31:25,807 ==>  Preparing: update t_user set user_name = ?, pass_word = ?, email = ?, td_id = ? where id = ?  (BaseJdbcLogger.java:137) 
DEBUG 08-10 15:31:25,845 ==> Parameters: dfgtb(String), fsdfsf(String), null, null, 6(Integer)  (BaseJdbcLogger.java:137) 

DEBUG 08-10 15:31:25,849 ==>  Preparing: select id, user_name, pass_word, email, td_id from t_user where id = ?  (BaseJdbcLogger.java:137) 
DEBUG 08-10 15:31:25,850 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 08-10 15:31:25,889 <==      Total: 1  (BaseJdbcLogger.java:137) 
//下面这个更新按理来说上面已经编译过一次,既然上次觉得会重用SQL,那么下次就应该直接进行参数处理,可是为什么下次又进行了一次编译?
DEBUG 08-10 15:31:25,891 ==>  Preparing: update t_user set user_name = ?, pass_word = ?, email = ?, td_id = ? where id = ?  (BaseJdbcLogger.java:137) 
DEBUG 08-10 15:31:25,891 ==> Parameters: dfhjgtb(String), fserfrg(String), null, null, 7(Integer)  (BaseJdbcLogger.java:137) 

源码解决问题?

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

BaseExecutor执行器

源码

BaseExecutor结构

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

测试

@Test
    public void BASET() throws SQLException {
        /*
        在BaseExecutor 的query和update中使用了缓存,这两个方法会调用doquery和doupdate方法(由子类实现)执行
        所以BaseExecutor和其子类在使用query和update才会调用一级缓存机制
         */

        BaseExecutor executor=new SimpleExecutor(configuration,transaction );
        MappedStatement statement=configuration.getMappedStatement("com.me.dao.UserMapper.selectByPrimaryKey");
    List<Object> query = executor.query(statement, 1, RowBounds.DEFAULT, SimpleExecutor.NO_RESULT_HANDLER);
    System.out.println(query.get(0));
    List<Object> query2=executor.query(statement,1,RowBounds.DEFAULT,SimpleExecutor.NO_RESULT_HANDLER);
    System.out.println(query2.get(0));
    }

执行

DEBUG 08-10 16:04:07,338 ==>  Preparing: select id, user_name, pass_word, email, td_id from t_user where id = ?  (BaseJdbcLogger.java:137) 
DEBUG 08-10 16:04:07,382 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 08-10 16:04:07,429 <==      Total: 1  (BaseJdbcLogger.java:137) 
User{id=1, userName='cheyuan', passWord='123456', email='dafa@qq.com', tdId=1}
//上次执行的结果保存到了一级缓存中,下次查询相同的内容,会直接从缓存中取出
User{id=1, userName='cheyuan', passWord='123456', email='dafa@qq.com', tdId=1}

在其中出入一条更新语句?

@Test
    public void BASET() throws SQLException {
        /*
        在BaseExecutor 的query和update中使用了缓存,这两个方法会调用doquery和doupdate方法(由子类实现)执行
        所以BaseExecutor和其子类在使用query和update才会调用一级缓存机制
         */

        BaseExecutor executor=new SimpleExecutor(configuration,transaction );
        MappedStatement statement=configuration.getMappedStatement("com.me.dao.UserMapper.selectByPrimaryKey");
    List<Object> query = executor.query(statement, 1, RowBounds.DEFAULT, SimpleExecutor.NO_RESULT_HANDLER);
    System.out.println(query.get(0));

    MappedStatement statement2=configuration.getMappedStatement("com.me.dao.UserMapper.updateByPrimaryKey");
    int update = executor.update(statement2, new User(6, "dfgtb", "fsdfsf", null, null));

    List<Object> query2=executor.query(statement,1,RowBounds.DEFAULT,SimpleExecutor.NO_RESULT_HANDLER);
    System.out.println(query2.get(0));
    }

执行

DEBUG 08-10 16:32:13,939 ==>  Preparing: select id, user_name, pass_word, email, td_id from t_user where id = ?  (BaseJdbcLogger.java:137) 
DEBUG 08-10 16:32:13,986 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 08-10 16:32:14,029 <==      Total: 1  (BaseJdbcLogger.java:137) 
User{id=1, userName='cheyuan', passWord='123456', email='dafa@qq.com', tdId=1}

DEBUG 08-10 16:32:14,047 ==>  Preparing: update t_user set user_name = ?, pass_word = ?, email = ?, td_id = ? where id = ?  (BaseJdbcLogger.java:137) 
DEBUG 08-10 16:32:14,049 ==> Parameters: dfgtb(String), fsdfsf(String), null, null, 6(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 08-10 16:32:14,050 <==    Updates: 1  (BaseJdbcLogger.java:137) 
//同样再次查询同样的内容,没有从一级缓存中取,而是有进行了一次编译查询,这是为什么?
DEBUG 08-10 16:32:14,051 ==>  Preparing: select id, user_name, pass_word, email, td_id from t_user where id = ?  (BaseJdbcLogger.java:137) 
DEBUG 08-10 16:32:14,052 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 08-10 16:32:14,053 <==      Total: 1  (BaseJdbcLogger.java:137) 
User{id=1, userName='cheyuan', passWord='123456', email='dafa@qq.com', tdId=1}

上面属于一级缓存失效,可以参考我的下一篇—缓存机制 o!

CachingExecutor执行器

测试

需要再mapper.xml开启缓存
在这里插入图片描述

@Test
    public void cacheT() throws SQLException {
        /*
        需要再mapper.xml开启缓存
         */
        Executor executor=new SimpleExecutor(configuration,transaction );
        Executor executor1=new CachingExecutor(executor);
        MappedStatement statement=configuration.getMappedStatement("com.me.dao.UserMapper.selectByPrimaryKey");
        List<Object> query = executor1.query(statement, 1, RowBounds.DEFAULT, SimpleExecutor.NO_RESULT_HANDLER);
        System.out.println(query.get(0));
        List<Object> query2 =executor1.query(statement,1,RowBounds.DEFAULT,SimpleExecutor.NO_RESULT_HANDLER);
        System.out.println(query2.get(0));
    }

执行

DEBUG 08-10 16:41:34,470 Cache Hit Ratio [com.me.dao.UserMapper]: 0.0  (LoggingCache.java:60) 
DEBUG 08-10 16:41:34,495 ==>  Preparing: select id, user_name, pass_word, email, td_id from t_user where id = ?  (BaseJdbcLogger.java:137) 
DEBUG 08-10 16:41:34,539 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 08-10 16:41:34,581 <==      Total: 1  (BaseJdbcLogger.java:137) 
User{id=1, userName='cheyuan', passWord='123456', email='dafa@qq.com', tdId=1}
//命中缓存,直接在缓存中取出
DEBUG 08-10 16:41:34,606 Cache Hit Ratio [com.me.dao.UserMapper]: 0.0  (LoggingCache.java:60) 
User{id=1, userName='cheyuan', passWord='123456', email='dafa@qq.com', tdId=1}

源码

在CachingExecutor 中包装了Executor(装饰者模式–可以参考博客->装饰者模式) ,CachingExecutor 只履行二级缓存的职责,将其他的操作交给了baseExecutor及其子类来实现,可以从源码中看出jdbc的操作都是有delegate的方法来执行的。

public class CachingExecutor implements Executor {

  private final Executor delegate;
  private final TransactionalCacheManager tcm = new TransactionalCacheManager();
//包装了executor
  public CachingExecutor(Executor delegate) {
    this.delegate = delegate;
    **delegate.setExecutorWrapper(this);
  }

  @Override
  public Transaction getTransaction() {
    return **delegate.getTransaction();
  }

  @Override
  public void close(boolean forceRollback) {
    try {
      // issues #499, #524 and #573
      if (forceRollback) {
        tcm.rollback();
      } else {
        tcm.commit();
      }
    } finally {
     ** delegate.close(forceRollback);
    }
  }

  @Override
  public boolean isClosed() {
    return ** delegate.isClosed();
  }

  @Override
  public int update(MappedStatement ms, Object parameterObject) throws SQLException {
    flushCacheIfRequired(ms);
    return **delegate.update(ms, parameterObject);
  }

  @Override
  public <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {
    flushCacheIfRequired(ms);
    return **delegate.queryCursor(ms, parameter, rowBounds);
  }

  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    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) {
      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;
      }
    }
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

为什么先执行二级缓存,再执行一级缓存?

测试

开启二级缓存

@Test
    public void twst() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("mybats.xml");
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.selectByPrimaryKey(2);
        System.out.println(user);
    }

执行

Connected to the target VM, address: '127.0.0.1:63104', transport: 'socket'
//命中l二级缓存
DEBUG 08-10 17:09:27,265 Cache Hit Ratio [com.me.dao.UserMapper]: 0.0  (LoggingCache.java:60) 
DEBUG 08-10 17:09:27,288 ==>  Preparing: select id, user_name, pass_word, email, td_id from t_user where id = ?  (BaseJdbcLogger.java:137) 
DEBUG 08-10 17:09:27,354 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 08-10 17:09:27,406 <==      Total: 1  (BaseJdbcLogger.java:137) 
User{id=1, userName='cheyuan', passWord='123456', email='dafa@qq.com', tdId=1}
Disconnected from the target VM, address: '127.0.0.1:63104', transport: 'socket'

经过debug发现,先进入CachingExecutor ,在进入simpleExecutor(默认)的
第一步:
在这里插入图片描述
第二步:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

霓乤

谢谢支持,菜鸟会继续努力..

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值