【十年老java都不一定知道的秘密】【mybatis sqlsession 一级缓存 什么时候创建的 什么时候销毁的】springboot+mysql+mybatis

前言背景

springboot 或者mvc 集成mybatis后。 不需要我们手动创建和销毁sqlsession,这会让人产生很大的困惑,sqlsession 究竟是什么时候创建的 什么时候销毁的。我怎么知道我的查询是否能够使用一级缓存。
如果我循环分页查询数据 不需要一级缓存来占用我们程序的内存 我们应该怎么处理来保证不使用一级缓存?
我搜索了很多相关资料,没有一处能讲的明白,所以那就我来验证一下。

结论

先说结论 sqlsession的创建和销毁 和事务有很大关系

  1. 如果是一个有事务的service方法,那么他们内部使用的是同一个sqlsession
  2. 如果是一个无事务的service方法,那么每调用一次mapper方法就创建一个sqlsession
  3. 在验证的过程中,我使用注解@Transactional(propagation = Propagation.NOT_SUPPORTED) 此时service方法是无事物的,但是我发现 它和有事务是一样的,service内部调用的使用的还是同一个sqlsession,此时比较特殊,不支持事务不代表不使用事务,是做了优化 可以使用同一个sqlsession,更进一步的没有深入查。。
  4. NOT_SUPPORTED的方法套用有事务的放,有事务的方法会单独创建sqlsession
  5. 可以直接配置mapper xml flushCache="true" useCache="false"来关闭一级缓存
  6. 可以在mapper层使用@Transactional(propagation = Propagation.REQUIRES_NEW) 来创建新事务 强制不使用一级缓存。

验证

验证前的准备

工程我放在git 上 ,可以clone 地址 https://gitee.com/shiihs/test-sqlsession.git
工程目录
在这里插入图片描述
新建库 执行db 即可跟我的数据一致
内有2个表 每个表都有对应的实体 mapper service
spring 配置如下:
开启了 org.mybatis: DEBUG

# 应用服务 WEB 访问端口
server:
  port: 8080
#下面这些内容是为了让MyBatis映射
#指定Mybatis的Mapper文件
mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.example.testsqlsession.mapper
#指定Mybatis的实体目录
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/wms-test?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
logging:
  level:
    org.mybatis: DEBUG

验证 有事务的方法 使用的是同一个sqlsession

测试入口

@Controller
public class BasicController {
    
    @Autowired
    private SkuService skuService;

    // 调用同一个service 的同一个方法 两遍 都是有事务的 看看创建了几个sqlsession
    @RequestMapping("/test")
    @ResponseBody
    public String test() {
        skuService.selectTransactional(1l);
        skuService.selectTransactional(1l);
        return "SUCCESS";
    }
}

service

    @Transactional
    @Override
    public TBaseSku selectTransactional(Long id) {
        // mapper 两个 是否相同
        TBaseSku tBaseSku = skuMapper.selectByPrimaryKey(id);
        TBaseSku tBaseSku2 = skuMapper.selectByPrimaryKey(id);
        // service 是否相同
        TBaseCustomer tBaseCustomer = customerService.selectByPrimaryKey(tBaseSku.getCustomerCode());
        TBaseCustomer tBaseCustomer2 = customerService.selectByPrimaryKey(tBaseSku.getCustomerCode());
        // 另外一个mapper  是否相同
        TBaseCustomer tBaseCustomer3 = customerMapper.selectByPrimaryKey(tBaseSku.getCustomerCode());
        TBaseCustomer tBaseCustomer4 = customerMapper.selectByPrimaryKey(tBaseSku.getCustomerCode());
        return tBaseSku;
    }

启动服务 调用 http://127.0.0.1:8080/test
在这里插入图片描述
可以看到 创建了2次sqlsession ,证明有事务的情况下 在事务里 多次查询相同数据 会使用一级缓存,但是controller 两次调用同一个service方法,是隔离开的,此时为了更加清晰的看到数据,让我们断点来看一下:
在这里插入图片描述

验证无事务的方法 每次调用dao 都创建一个新的sqlsession

controller

    @RequestMapping("/test2")
    @ResponseBody
    public String test2() {
        skuService.selectNoTransactional(1l);
        return "SUCCESS";
    }

service

// 我使用的是注解事务 没有注解就没有事务
    @Override
    public TBaseSku selectNoTransactional(Long id) {
        // mapper 两个 是否相同
        TBaseSku tBaseSku = skuMapper.selectByPrimaryKey(id);
        TBaseSku tBaseSku2 = skuMapper.selectByPrimaryKey(id);
        // service 是否相同
        TBaseCustomer tBaseCustomer = customerService.selectByPrimaryKey(tBaseSku.getCustomerCode());
        TBaseCustomer tBaseCustomer2 = customerService.selectByPrimaryKey(tBaseSku.getCustomerCode());
        // 另外一个mapper  是否相同
        TBaseCustomer tBaseCustomer3 = customerMapper.selectByPrimaryKey(tBaseSku.getCustomerCode());
        TBaseCustomer tBaseCustomer4 = customerMapper.selectByPrimaryKey(tBaseSku.getCustomerCode());
        return tBaseSku;
    }

此时调用 http://127.0.0.1:8080/test2 查看日志
在这里插入图片描述
发现打印了6遍 证明我们创建了6个seqsession 我们断点看一眼
在这里插入图片描述

验证 @Transactional(propagation = Propagation.NOT_SUPPORTED) 也用了一个sqlsession

前言: 本来我的验证就到此结束了,但是我在实际使用的时候想,怎么样让一个方法无事务?
此时我想到了此注解,但是使用此注解后 我发现 还是同一个sqlsession,这引起了我的兴趣。更底层的原因大家可以自行寻找一下 有结果可以私聊。
我们来验证一下:
controller

    @RequestMapping("/test3")
    @ResponseBody
    public String test3() {
        skuService.selectNotSupportTransactional(1l);
        return "SUCCESS";
    }

service

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    @Override
    public TBaseSku selectNotSupportTransactional(Long id) {
        // mapper 两个 是否相同
        TBaseSku tBaseSku = skuMapper.selectByPrimaryKey(id);
        TBaseSku tBaseSku2 = skuMapper.selectByPrimaryKey(id);

        TBaseCustomer tBaseCustomer = customerService.selectByPrimaryKey(tBaseSku.getCustomerCode());
        TBaseCustomer tBaseCustomer2 = customerService.selectByPrimaryKey(tBaseSku.getCustomerCode());

        TBaseCustomer tBaseCustomer3 = customerMapper.selectByPrimaryKey(tBaseSku.getCustomerCode());
        TBaseCustomer tBaseCustomer4 = customerMapper.selectByPrimaryKey(tBaseSku.getCustomerCode());
        return tBaseSku;
    }

调用 http://127.0.0.1:8080/test3 查看日志
在这里插入图片描述
此时断点查看
在这里插入图片描述
那么问题来了哈,如果我在不支持和事物的方法里套用有事务的方法 那么会和创建几个sqlsession

不支持事务的方法 套有事务的方法,有事务的方法会单独创建一个sqlsession

controller

    @RequestMapping("/test4")
    @ResponseBody
    public String test4() {
        skuService.selectNotSupportTransactional2(1l);
        return "SUCCESS";
    }

service

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    @Override
    public TBaseSku selectNotSupportTransactional2(Long id) {
        // mapper 两个 是否相同
        TBaseSku tBaseSku = skuMapper.selectByPrimaryKey(id);
        TBaseSku tBaseSku2 = skuMapper.selectByPrimaryKey(id);

        TBaseCustomer tBaseCustomer = customerService.selectTransactional(tBaseSku.getCustomerCode());
        TBaseCustomer tBaseCustomer2 = customerService.selectTransactional(tBaseSku.getCustomerCode());

        TBaseCustomer tBaseCustomer3 = customerMapper.selectByPrimaryKey(tBaseSku.getCustomerCode());
        TBaseCustomer tBaseCustomer4 = customerMapper.selectByPrimaryKey(tBaseSku.getCustomerCode());
        return tBaseSku;
    }

执行 http://127.0.0.1:8080/test4 看日志
在这里插入图片描述
断点查看
在这里插入图片描述

配置xml 强制的创建新的sqlsession

xml配置

    <select id="selectNoCache" parameterType="java.lang.Long" resultMap="BaseResultMap" flushCache="true" useCache="false">
        select
        <include refid="Base_Column_List" />
        from t_base_sku
        where  pk_base_sku_id = #{pkBaseSkuId,jdbcType=BIGINT}
    </select>

controller

    @RequestMapping("/test5")
    @ResponseBody
    public String test5() {
        skuService.selectNotSupportTransactional2(1l);
        return "SUCCESS";
    }

service

    @Transactional
    @Override
    public TBaseSku selectTransactional2(Long id) {
        // mapper 两个 是否相同
        TBaseSku tBaseSku = skuMapper.selectRequiresNew(id);
        TBaseSku tBaseSku2 = skuMapper.selectRequiresNew(id);
        // 另外一个mapper  是否相同
        TBaseCustomer tBaseCustomer3 = customerMapper.selectByPrimaryKey(tBaseSku.getCustomerCode());
        TBaseCustomer tBaseCustomer4 = customerMapper.selectByPrimaryKey(tBaseSku.getCustomerCode());
        return tBaseSku;
    }

请求 日志 可以看到 创建了一个sqlsession
在这里插入图片描述
但是因为刷新 并没有保留一级缓存 我们看看断点
在这里插入图片描述

最后我们来看使用注解 来床架新的sqlsession 来不使用一级缓存的

controller

    @RequestMapping("/test6")
    @ResponseBody
    public String test6() {
        skuService.selectTransactional3(1l);
        return "SUCCESS";
    }

service

    @Transactional
    @Override
    public TBaseSku selectTransactional3(Long id) {
        // mapper 两个 是否相同
        TBaseSku tBaseSku = skuMapper.selectRequiresNew(id);
        TBaseSku tBaseSku2 = skuMapper.selectRequiresNew(id);
        // 另外一个mapper  是否相同
        TBaseCustomer tBaseCustomer3 = customerMapper.selectByPrimaryKey(tBaseSku.getCustomerCode());
        TBaseCustomer tBaseCustomer4 = customerMapper.selectByPrimaryKey(tBaseSku.getCustomerCode());
        return tBaseSku;
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    TBaseSku selectRequiresNew(Long id);

调用 http://127.0.0.1:8080/test6 查看日志
在这里插入图片描述
断点查看
在这里插入图片描述
可以看到 新的事务也能创建新的sqlsession ,一级缓存 是sqlsession级别的 所以,棒哇
到此就结束了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值