MyBatis的localCache问题及解决方案

背景

在单元测试中,我们需要验证不同的场景下系统功能是否正常。而构造不同的测试场景,就需要对db中的数据做相应的订正,同时通过事务的回滚,保证单元测试结束后,db中的数据回归到初始状态

问题描述

单元测试类上已经申明了@Transactional注解,在下面的这个测试场景代码中

result1 = queryResult();
jdbcTemplate.update("***");
jdbcTemplate.update("***");
result2 = queryResult();

result1和result2的结果完全一致,不符合预期

原因探讨

mybatis默认开启了localCache功能,默认的scope是session级别。那么在同一个session中,相同的Query重复执行的时候,直接从缓存中读取结果。

  @SuppressWarnings("unchecked")
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ……
    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--;
    }
    ……
    return list;
  }

解决方案

只需要在select element中加上flushCache=”true”,则这个statement执行结束后,会将localCache清空,下一次查询的时候就会直接查询数据库

    <select id="selectAll" resultMap="BaseResultMap" flushCache="true">
        select
        <include refid="Base_Column_List"/>
        from Table
    </select>

如果配置了这个选项,则在执行之前会将缓存清空,保证从数据库重新查询一次

  @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);
      ……
  }

  private void flushCacheIfRequired(MappedStatement ms) {
    Cache cache = ms.getCache();
    if (cache != null &amp;&amp; ms.isFlushCacheRequired()) {      
      tcm.clear(cache);
    }
  }

不过需要注意的是,mybatis默认的作用域是session级别,也就是说清空以后,会影响这个session内的所有sql。
可以在mybatis的配置中,配置cache的作用域是session级别还是sql级别

<setting name="localCacheScope" value="SESSION"/>
<setting name="localCacheScope" value="STATEMENT"/>

免费启用https

浏览到coolshell上的一篇博客,可以免费启用https,赶紧按步骤体验了下。

总体流程比较顺利,也顺利创建了crontab任务

ubuntu@:~$ cat /etc/cron.d/certbot
# /etc/cron.d/certbot: crontab entries for the certbot package
#
# Upstream recommends attempting renewal twice a day
#
# Eventually, this will be an opportunity to validate certificates
# haven't been revoked, etc.  Renewal will only occur if expiration
# is within 30 days.
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

0 */12 * * * root test -x /usr/bin/certbot -a \! -d /run/systemd/system &amp;&amp; perl -e 'sleep int(rand(3600))' &amp;&amp; certbot -q renew

但是http自动跳转到https并没有起作用,后来在网上找到了类似的问题,用下面的配置启用了http强制跳转

ServerName njujiang.tech
ServerAdmin njujiang@163.com
DocumentRoot /var/www/html/wordpress
Redirect / https://njujiang.tech/