文章目录
前言背景
springboot 或者mvc 集成mybatis后。 不需要我们手动创建和销毁sqlsession,这会让人产生很大的困惑,sqlsession 究竟是什么时候创建的 什么时候销毁的。我怎么知道我的查询是否能够使用一级缓存。
如果我循环分页查询数据 不需要一级缓存来占用我们程序的内存 我们应该怎么处理来保证不使用一级缓存?
我搜索了很多相关资料,没有一处能讲的明白,所以那就我来验证一下。
结论
先说结论 sqlsession的创建和销毁 和事务有很大关系
- 如果是一个有事务的service方法,那么他们内部使用的是同一个sqlsession
- 如果是一个无事务的service方法,那么每调用一次mapper方法就创建一个sqlsession
- 在验证的过程中,我使用注解
@Transactional(propagation = Propagation.NOT_SUPPORTED)
此时service方法是无事物的,但是我发现 它和有事务是一样的,service内部调用的使用的还是同一个sqlsession,此时比较特殊,不支持事务不代表不使用事务,是做了优化 可以使用同一个sqlsession,更进一步的没有深入查。。 - NOT_SUPPORTED的方法套用有事务的放,有事务的方法会单独创建sqlsession
- 可以直接配置mapper xml
flushCache="true" useCache="false"
来关闭一级缓存 - 可以在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级别的 所以,棒哇
到此就结束了。