MyBatis框架一级缓存与二级缓存
为了提升查询效率,提高用户体验,MyBatis提供了数据缓存支持,依据数据缓存的有效范围默认定义了一级缓存和二级缓存
一级缓存
1、该级缓存默认开启,不能关闭;
2、该级缓存为SqlSession级别的缓存,也称为本地缓存;
3、以下4种情况将会导致该级缓存失效:
a、在不同SqlSession中查询数据;
public class Test { public static void main(String[] args) { try { InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(true);//为true自动提交,默认false开启事务需要手动commit IUserInfoDao userInfoDao = sqlSession.getMapper(IUserInfoDao.class); List<UserInfo>list = userInfoDao.select(); System.out.println(list.size()); list = userInfoDao.select(); System.out.println(list.size()); sqlSession.close(); sqlSession = sqlSessionFactory.openSession(); userInfoDao = sqlSession.getMapper(IUserInfoDao.class); list = userInfoDao.select(); System.out.println(list.size()); sqlSession.close(); } catch (IOException e) { e.printStackTrace(); } } }
执行结果:同一个sqlSession中缓存成功,不同的sqlSession缓存失效
b、相同SqlSession中查询数据,但查询条件不同
public class Test { public static void main(String[] args) { try { InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(true);//为true自动提交,默认false开启事务需要手动commit IUserInfoDao userInfoDao = sqlSession.getMapper(IUserInfoDao.class); List<UserInfo>list = userInfoDao.select("%阿%"); System.out.println(list.size()); list = userInfoDao.select("%a%"); System.out.println(list.size()); sqlSession.close(); } catch (IOException e) { e.printStackTrace(); } } }
执行结果:
c、相同SqlSession中查询数据,但两次查询之间执行了增删改操作
public class Test { public static void main(String[] args) { try { InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(true);//为true自动提交,默认false开启事务需要手动commit IUserInfoDao userInfoDao = sqlSession.getMapper(IUserInfoDao.class); List<UserInfo>list = userInfoDao.select(); System.out.println(list.size()); boolean flag = userInfoDao.delete("1"); System.out.println(flag); list = userInfoDao.select(); System.out.println(list.size()); sqlSession.close(); } catch (IOException e) { e.printStackTrace(); } } }
执行结果:
d、相同SqlSession中查询数据,但第二次查询前,程序调用SqlSession对象clearCache()方法手动清除了一级缓存
public class Test { public static void main(String[] args) { try { InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(true);//为true自动提交,默认false开启事务需要手动commit IUserInfoDao userInfoDao = sqlSession.getMapper(IUserInfoDao.class); List<UserInfo>list = userInfoDao.select(); System.out.println(list.size()); sqlSession.clearCache(); list = userInfoDao.select(); System.out.println(list.size()); sqlSession.close(); } catch (IOException e) { e.printStackTrace(); } } }
执行结果:
二级缓存
1、该级缓存默认不开启,但如果使用二级缓存需要在每个XML映射文件中添加<cache></cache>以配置该级缓存(相应实体类要序列化)。二级缓存可以通过在全局配置文件配置setting标签来关闭该级缓存。
如果这样配置的话,很多其他的配置就会被默认进行,如:
- 映射文件所有的select 语句会被缓存
- 映射文件的所有的insert、update和delete语句会刷新缓存
- 缓存会使用默认的Least Recently Used(LRU,最近最少使用原则)的算法来回收缓存空间
- 根据时间表,比如No Flush Interval,(CNFI,没有刷新间隔),缓存不会以任何时间顺序来刷新
- 缓存会存储列表集合或对象(无论查询方法返回什么)的1024个引用
- 缓存会被视为是read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以很安全的被调用者修改,不干扰其他调用者或县城所作的潜在修改
可以在开启二级缓存时候,手动配置一些属性
<cache eviction="LRU" flushInterval="100000" size="1024" readOnly="true"/>
各个属性意义如下:
- eviction:缓存回收策略
- LRU:最少使用原则,移除最长时间不使用的对象
- FIFO:先进先出原则,按照对象进入缓存顺序进行回收
- SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象
- WEAK:弱引用,更积极的移除移除基于垃圾回收器状态和弱引用规则的对象 - flushInterval:刷新时间间隔,单位为毫秒,这里配置的100毫秒。如果不配置,那么只有在进行数据库修改操作才会被动刷新缓存区
- size:引用额数目,代表缓存最多可以存储的对象个数
- readOnly:是否只读,如果为true,则所有相同的sql语句返回的是同一个对象(有助于提高性能,但并发操作同一条数据时,可能不安全),如果设置为false,则相同的sql,后面访问的是cache的clone副本。
2、该级缓存为namespace级别的缓存
3、工作机制:通过SqlSession查询数据,这些数据将会放到当前会话的一级缓存中;如果当前会话关闭,则一级缓存中的数据会被保存到二级缓存中,此后新的SqlSession将从二级缓存中查找数据;
public class Test { public static void main(String[] args) { try { InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(true);//为true自动提交,默认false开启事务需要手动commit IUserInfoDao userInfoDao = sqlSession.getMapper(IUserInfoDao.class); List<UserInfo>list = userInfoDao.select(); System.out.println(list.size()); sqlSession.close(); sqlSession = sqlSessionFactory.openSession(); userInfoDao = sqlSession.getMapper(IUserInfoDao.class); list = userInfoDao.select(); System.out.println(list.size()); sqlSession.close(); } catch (IOException e) { e.printStackTrace(); } } }
执行结果:
4、select标签的useCache属性用于设置是否使用二级缓存;insert、update、delete或select标签均有flushCache属性,其中增删改默认true,即sql执行以后,会同时清空一级和二级缓存,查询默认false。
public class Test { public static void main(String[] args) { try { InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(true);//为true自动提交,默认false开启事务需要手动commit IUserInfoDao userInfoDao = sqlSession.getMapper(IUserInfoDao.class); List<UserInfo>list = userInfoDao.select(); System.out.println(list.size()); sqlSession.close(); sqlSession = sqlSessionFactory.openSession(); userInfoDao = sqlSession.getMapper(IUserInfoDao.class); boolean flag = userInfoDao.delete("1");//清空二级缓存 System.out.println(flag); list = userInfoDao.select(); System.out.println(list.size()); sqlSession.close(); } catch (IOException e) { e.printStackTrace(); } } }
执行结果:
5、为了提高扩展性,MyBatis定义了Cache缓存接口,可以通过实现该缓存接口自定义二级缓存,jar包下载:https://github.com/mybatis。
为了提升查询效率,提高用户体验,MyBatis提供了数据缓存支持,依据数据缓存的有效范围默认定义了一级缓存和二级缓存