mybatis一级缓存的作用域是同一个sqlsession,在同一个sqlsession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写入到缓存(内存),第二次会从缓存中获取,从而提高查询效率。当一个sqlsession结束后该sqlsession中的以及缓存也就不存在了。mybatis默认开启一级缓存。
Mybatis二级缓存是多个sqlsession共享的,其作用域是mapper的同一个namespace,不同的sqlsession两次执行相同的namespace下的sql语句且向sql中传递参数也相同即最终执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。Mybatis默认没有开启二级缓存,需要在setting全局参数中配置开启二级缓存。
区别:
一级缓存区域是根据sqlsession为单位划分的。每次查询会先从缓存区域找,如果找不到从数据库查询,查询到数据将数据写入缓存。mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+sql语句。value为查询出来映射生成的java对象sqlsession执行insert、update、delete等操作的commit提交后会清空缓存区域。
@Test
public void Test(){
SqlSession sqlSession = MybatisUtils.openSession();
CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class);
Customer customer = customerMapper.getCustomerWithId(2);
System.out.println("Customer1----"+customer);
Customer customer2 = customerMapper.getCustomerWithId(2);
System.out.println("Customer1----"+customer2);
System.out.println("是否相等:"+(customer2==customer));
sqlSession.close();
}
上图显示的命中率为0,是因为我们配置开启了二级缓存,显示的是二级缓存的命中率。查询顺序:二级缓存---》一级缓存---》数据库;
<setting name="cacheEnabled" value="true"/>
然后在对于的mapper.xml里面加入配置
<cache eviction="FIFO" flushInterval="6000" readOnly="false" size="50" >
</cache>
由上图可以看出来,二级缓存是有设置定时情况缓存的时间的,也有设置对应的回收策略,这也很好的解答了上面的疑问(二级缓存什么时候失效)
使用动态代理的方式:
@Test
public void Test2(){
SqlSession sqlSession = MybatisUtils.openSession();
CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class);
Customer customer = customerMapper.getCustomerWithId(2);
System.out.println("customer=="+customer);
sqlSession.close();
SqlSession sqlSession2= MybatisUtils.openSession();
CustomerMapper customerMapper2 = sqlSession2.getMapper(CustomerMapper.class);
Customer customer2 = customerMapper2.getCustomerWithId(2);
System.out.println("customer2=="+customer2);
System.out.println("是否相等:"+(customer2==customer));
sqlSession2.close();
}
@Test
public void Test3(){
SqlSession sqlSession = MybatisUtils.openSession();
Customer customer = sqlSession.selectOne("getCustomerWithId", 2);
System.out.println("customer=="+customer);
sqlSession.close();
SqlSession sqlSession2= MybatisUtils.openSession();
Customer customer2 = sqlSession2.selectOne("getCustomerWithId", 2);
System.out.println("customer2=="+customer2);
System.out.println("是否相等:"+(customer2==customer));
sqlSession2.close();
}
运行结果如下图所示:
结果与上图执行结果一样,可以得知缓存与何种方式执行sql无关:
再次执行上面的代码,运行结果直接就异常了:
这个报错的信息是显示实体类必须序列化才行,调整实体类:
再次进行执行结果如下:
运行结果成功,并且只执行了一次sql,第二次执行查询缓存命中,但是查询出来的两个对象却不相等了。
通过设置查询的cache属性上面可以得知,readonly:是否只读,true代表只读,mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。mybatis为了加快获取速度,直接会将数据在缓存中的引用交给用户,不安全,但是速度很快;false:非只读,mybatis觉得获取的数据可能会被修改mybatis会利用序列化或者反序列化的方式克隆一份新的数据给到用户,这样比较安全,但是速度慢。
由于上面说到mybatis会用到序列化反序列化的技术克隆一份新的数据,所以对于的pojo需要实现序列化。并且既然得到的是复制的一份新的数据,所以查询的对象并不相等了,这也可以算法和一级缓存的差别了。