Spring整合mybatis后,在注入每个Mapper的时候,都是注入的由mybatis生成的代理对象,在执行每个sql时都是由这个代理对象去执行的,而在生成代理对象的时候,获取的SqlSession是通过一个SqlSessionTemplate生成的一个SqlSessionProxy对象,也就是说在执行比如select类sql的时候都是由这个SqlSessionProxy对象去执行具体sqlSession的selectOne方法去查询数据库的,但是在获取具体的SqlSession的时候会有一个判断,就是判断有没有开启事务(加没加@Transactional注解),如果开启了事务,就会先去ThreadLocal的属性中去取,如果可以取到就直接用,取不到就新建,这样多个sql就会共用一个SqlSession,这时一级缓存就生效了;如果没有开启事务,则每次都是新建SqlSession,这样的话,就是当一个sql执行完后,这个SqlSession的生命周期就完成啦,下一个sql进来又是新的SqlSession,这也就不存在缓存的意义了。这也说明一级缓存失效其实并不是个问题,而是本身设计就是这样的。
其实并不建议开启一级缓存,因为当一级缓存开启后,在一个事务中去执行相同的sql,比如:
@Transactional //当事务的隔离级别设置为读未提交
public void test(){
System.out.println(userMapper.findById());①
System.out.println(userMapper.findById());②
}
这时候就会出现这样的情况:
①查询的结果比如是2条数据;但在①执行完,②未执行前,
又在数据库中增加了一条数据,本来②应该查出3条数据,
但因为存在一级缓存,②会直接返回①的结果,这就不是我们想要的结果
当然这还有个问题就是mybatis在源码实现上都没有加Lock,synchronized等加锁机制,为了保证线程安全,这使用了ThreadLocal来修饰一些属性,这就保证了在一个事务中,是用的同一个Sqlsession,从而保证线程安全。