今天看了mybatis二级缓存的原理,发现原来是session关闭时更新二级缓存。忽然想到这样的话缓存中就是最近关闭的session中的一级缓存数据,而不是最新的数据。于是做了实验,果然证实。这样子的话使用mybatis二级缓存貌似会很危险。因为并发访问情况下各个sqlsession关闭的时间顺序并不能确保。下面是测试代码片段((mybatis自带的二级缓存,配置:LRU算法)
//初始时,数据库中id为12的员工为王老三
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession sqlSession = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
SqlSession sqlSession3 = sqlSessionFactory.openSession();
SqlSession sqlSession4 = sqlSessionFactory.openSession();
EmployeesMapper mapper = sqlSession.getMapper(EmployeesMapper.class);
EmployeesMapper mapper2 = sqlSession2.getMapper(EmployeesMapper.class);
EmployeesMapper mapper3 = sqlSession3.getMapper(EmployeesMapper.class);
EmployeesMapper mapper4 = sqlSession4.getMapper(EmployeesMapper.class);
EmployeesBean emp = mapper.getEmployee(12);
System.out.println("emp:"+emp); //找到王老三
mapper2.updateEmp(new EmployeesBean(12, "老四", "李", "北京")); //更新为李老四
sqlSession2.commit(); //提交李老四
EmployeesBean emp2 = mapper2.getEmployee(12); //从数据库查找李老四
System.out.println("emp2:"+emp2); //显示李老四
sqlSession2.close(); //李老四进入二级缓存
EmployeesBean emp3 = mapper3.getEmployee(12); //从缓存找到李老四
System.out.println("emp3:"+emp3);
sqlSession3.close(); //二级缓存更新为李老四
sqlSession.close(); //二级缓存更新为王老三
EmployeesBean emp4 = mapper4.getEmployee(12); //难道是王老三?
System.out.println("emp4:"+emp4); //果然又是你,王老三
sqlSession4.close();
输出结果如下(把日志前半部分省略了, **为本人追加的注解):
...] - Cache Hit Ratio [com.testmybatis.EmployeesMapper]: 0.0
...] - ==> Preparing: select id, last_name, first_name, city from Employees where id = ?
...] - ==> Parameters: 12(Integer)
...] - <== Total: 1
emp:EmployeesBean{id=12, last_name='老三', first_name='王', city='西安'} **找到王老三
...] - ==> Preparing: update Employees set last_name=?, first_name=?, city=? where id=?
...] - ==> Parameters: 老四(String), 李(String), 北京(String), 12(Integer)
...] - <== Updates: 1 **更新为李老四
...] - Cache Hit Ratio [com.testmybatis.EmployeesMapper]: 0.0
...] - ==> Preparing: select id, last_name, first_name, city from Employees where id = ?
...] - ==> Parameters: 12(Integer)
...] - <== Total: 1
emp2:EmployeesBean{id=12, last_name='老四', first_name='李', city='北京'} **找到李老四
...] - Cache Hit Ratio [com.testmybatis.EmployeesMapper]: 0.3333333333333333
emp3:EmployeesBean{id=12, last_name='老四', first_name='李', city='北京'} **从二级缓存找到李老四
...] - Cache Hit Ratio [com.testmybatis.EmployeesMapper]: 0.5
emp4:EmployeesBean{id=12, last_name='老三', first_name='王', city='西安'} **从二级缓存又找到王老三
Process finished with exit code 0
补充:利用mybatis-encache代替mybatis自带的缓存,测试结果没有发生上述问题,具体原因待空闲时看个究竟。
缓存修改为mybatis-ehcache后,问题依旧。之前误以为没问题了,是因为数据修改后没有复原,再次运行结果一直是李老四。