首页 > 其他 > 详细

Mybatis的一级缓存和二级缓存

时间:2020-04-07 13:32:46      阅读:86      评论:0      收藏:0      [点我收藏+]

一、一级缓存

  缓存是存在于内存中的临时数据,使用缓存的目的是为了减少和数据库的交互次数,提高执行效率。Mybatis中默认开启一级缓存。一级缓存指的是MybatisSQLSession对象的缓存。当我们执行查询之后,查询的结果会存入到SQLSession为我们提供个一块内存区域中,该区域的结构是一个map,当我们再次查询同样的数据时Mybatis会先去SqlSession中查询是否已经存在,存在的话则直接从缓存在中拿出数据。当SqlSession对象消失时,Mybatis的一级缓存随之消失。

测试一级缓存:

 1 public class MybatisTest {
 2     private InputStream resourceAsStream;
 3     private SqlSessionFactory factory;
 4     private SqlSession sqlSession;
 5     private StudentDao studentDao;
 6 
 7     //用于在测试方法之前执行
 8     @Before
 9     public void init() throws IOException {
10         //1、读取配置文件,生成字节输入流
11         resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
12         //2、获取SqlSessionFactory
13         factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
14         //3、获取SqlSession对象
15         sqlSession = factory.openSession(true);
16         //获取dao的代理对象
17         studentDao = sqlSession.getMapper(StudentDao.class);
18 
19     }
20 
21     @After
22     public void destroy() throws IOException {
23         resourceAsStream.close();
24     }
25 
26     @Test
27     public void testFirstLevelCache(){
28 
29         /**
30          * 一级缓存是sqlSession范围的缓存,当调用sqlSession的修改、添加、删除、commit(),close()等方法时,会清空一级缓存
31          */
32         /**
33          * 第一次查询id为00000000的信息
34          */
35         Student student = studentDao.queryById("00000000");
36         System.out.println(student);
37 
38         //在两次查询之间不执行sqlSession的修改、添加、删除、commit(),close()等操作
39         /**
40          * 第二次查询Id为00000000的信息
41          */
42         Student student1 = studentDao.queryById("00000000");
43         System.out.println(student1);
44 
45         /**
46          * 比较两次查询获取的是否为同一个对象
47          */
48         System.out.println(student ==  student1);
49 
50     }
51 
52 }

查询结果为:

[QC] DEBUG [main] org.apache.ibatis.logging.LogFactory.setImplementation(105) | Logging initialized using ‘class org.apache.ibatis.logging.log4j.Log4jImpl‘ adapter.
[QC] DEBUG [main] org.apache.ibatis.datasource.pooled.PooledDataSource.forceCloseAll(353) | PooledDataSource forcefully closed/removed all connections.
[QC] DEBUG [main] org.apache.ibatis.datasource.pooled.PooledDataSource.forceCloseAll(353) | PooledDataSource forcefully closed/removed all connections.
[QC] DEBUG [main] org.apache.ibatis.datasource.pooled.PooledDataSource.forceCloseAll(353) | PooledDataSource forcefully closed/removed all connections.
[QC] DEBUG [main] org.apache.ibatis.datasource.pooled.PooledDataSource.forceCloseAll(353) | PooledDataSource forcefully closed/removed all connections.
[QC] DEBUG [main] org.apache.ibatis.cache.decorators.LoggingCache.getObject(60) | Cache Hit Ratio [com.dao.StudentDao]: 0.0
[QC] DEBUG [main] org.apache.ibatis.transaction.jdbc.JdbcTransaction.openConnection(136) | Opening JDBC Connection
[QC] DEBUG [main] org.apache.ibatis.datasource.pooled.PooledDataSource.popConnection(424) | Created connection 1943325854.
[QC] DEBUG [main] org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(143) | ==>  Preparing: SELECT * FROM STUDENT T WHERE T.ID=? 
[QC] DEBUG [main] org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(143) | ==> Parameters: 00000000(String)
[QC] DEBUG [main] org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(143) | <==      Total: 1
第一次查询:com.domain.Student@40f08448
[QC] DEBUG [main] org.apache.ibatis.cache.decorators.LoggingCache.getObject(60) | Cache Hit Ratio [com.dao.StudentDao]: 0.0
第二次查询:com.domain.Student@40f08448
true

从输出的日志结果看,只有第一次查询的时候与数据库进行了交互,第二次查询直接从缓存中取出数据。

一级缓存的生命周期:

1)、如果SqlSession调用了close()方法,则会释放掉一级缓存对象,一级缓存将不可用。
2)、如果SqlSession调用了clearCache()方法或执行了一个update操作(包括update()、delete()、insert()),都会清空一级缓存对象的数据,但是该对象可以继续使用

 

二、二级缓存

 二级缓存是基于namespace级别的缓存,一个namespace对应一个二级缓存。默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。要启用全局的二级缓存:
  1)、需要在Mybatis-config.xml中设置开启,
  2)、需要在SQL的映射文件(即Mapper.xml)中设置开启。

   1、在Mybatis-config.xml中设置开启二级缓存

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 3         "http://mybatis.org/dtd/mybatis-3-config.dtd">
 4 <!--mybatis的主配置文件-->
 5 <configuration>
 6     <settings>
 7         <!--开启二级缓存-->
 8         <setting name="cacheEnabled" value="true"/>
 9     </settings>
10 </configuration>

     2、在SQL的映射文件中设置开启二级缓存

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 3         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 4 <mapper namespace="com.dao.StudentDao">
 5     <!--开启namespace下的二级缓存-->
 6     <cache/>
 7 
 8     <!--配置查询所有-->
 9     <select id="queryAll" resultType="com.domain.Student">
10         SELECT * FROM STUDENT
11     </select>
12     ......
13 
14 </mapper>

添加简单的一行<cache/>就可以开启二级缓存,这个简单语句的效果如下:
· 映射语句文件中的所有select语句的结果将会被缓存。
· 映射语句文件中的所有insert、updatedelete语句会刷新缓存。
· 缓存会使用最近最少使用算法(LRU, Least Recently Used)来清除不需要的缓存。
· 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
· 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
· 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

 这些默认效果可以通过cache元素的属性来修改:

1 <cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024"/>

· 说明:
  eviction:指缓存的回收策略,默认为LRU。Mybatis支持的清除策略有:
     1)、LRU-最近最少使用:移除最长时间不被使用的对象。
    2)、FIFO-先进先出:按对象进入缓存的顺序来移除它们。
    3)、SOFT-软引用:基于垃圾回收器状态和软引用规则移除对象。
    4)、WEAK-弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
  lushInterval:刷新间隔,其值是一个以毫秒为单位的任意正整数的合理时间量。默认情况是不设置,也就是没有刷新间隔,即不刷新。
  size:引用数目。其值为任意正整数,需要注意欲缓存对象的大小和运行环境中可用的内存资源。默认是1024.
  readOnly:缓存是否只读(true|false),默认为false。为true表示缓存对象只能读而不能被修改,其性能比较可观,只读的缓存会给所有调用者返回缓存对象的相同实例。为false表示缓存对象可被修改,可读写的缓存会(通过序列化)返回对象的拷贝。其速度上会慢一点,但是更安全。

注意:二级缓存是事务性的。这意味着,当SqlSession完成并提交时,或是完成并回滚,但没有执行flushCache=trueinsert/delete/update语句时,缓存会获得更新。使用二级缓存的对象需要序列化(即实现Serializable)。

  缓存的配置和缓存实例会被绑定到 SQL 映射文件的命名空间中。 因此,同一命名空间中的所有语句和缓存将通过命名空间绑定在一起。 每条语句可以自定义与缓存交互的方式,或将它们完全排除于缓存之外,这可以通过在每条语句上使用两个简单属性来达成。 默认情况下,语句会这样来配置:

1 <select ... flushCache="false" useCache="true"/>
2 <insert ... flushCache="true"/>
3 <update ... flushCache="true"/>
4 <delete ... flushCache="true"/>

  默认情况下,执行select是使用二级缓存,并不会清空二级缓存;执行insert|update|delete 时都会清空二级缓存。如果你想改变默认的行为,只需要设置flushCacheuseCache 属性即可。

  3、使用自定义缓存

  除了使用Mybatis本身的一级缓存和二级缓存外,还可以通过实现自己的缓存或为其他第三方缓存方案创建适配器来完全覆盖缓存行为。

1 <cache type="com.domain.something.MyCustomCache"/>

  type 属性指定的类必须实现 org.apache.ibatis.cache.Cache接口,且提供一个接受 String参数作为id的构造器。

  为了实现对缓存的灵活配置,可以通过在缓存实现中添加公有的JavaBean属性,然后通过cache元素传递属性值。下面的例子将在缓存实现上调用一个名为 setCacheFile(String file)的方法:

1 <cache type="com.domain.something.MyCustomCache">
2   <property name="cacheFile" value="/tmp/my-custom-cache.tmp"/>
3 </cache>

  可以使用所有简单类型作为JavaBean属性的类型,MyBatis会进行转换。 也可以使用占位符${cache.file},以便替换成在配置文件属性中定义的值。

     注意:对缓存的配置(如:清除策略、可读或可读写等),不能适用于自定义缓存中。

  4、缓存配置的共享引用

  对某一命名空间的语句,只会使用该命名空间的缓存进行缓存或刷新。但如果想要在多个命名空间中共享相同的缓存配置和实例。要实现这种需求,可以使用 cache-ref 元素来引用另一个缓存:

1 <cache-ref namespace="com.someone.application.data.SomeMapper"/>

   二级缓存的测试:

 1 public class MybatisTest {
 2     private InputStream resourceAsStream;
 3     private SqlSessionFactory factory;
 4     private SqlSession sqlSession;
 5     private StudentDao studentDao;
 6 
 7     //用于在测试方法之前执行
 8     @Before
 9     public void init() throws IOException {
10         //1、读取配置文件,生成字节输入流
11         resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
12         //2、获取SqlSessionFactory
13         factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
14         //3、获取SqlSession对象
15         sqlSession = factory.openSession(true);
16         //获取dao的代理对象
17         studentDao = sqlSession.getMapper(StudentDao.class);
18 
19     }
20 
21     @After
22     public void destroy() throws IOException {
23         resourceAsStream.close();
24     }
25 
26     @Test
27     public void testSecondLevelCache(){
28 
29         /**
30          * 在开启二级缓存的情况下执行两侧查询来测试二级缓存
31          */
32         Student student = studentDao.queryById("00000000");
33         System.out.println("第一次查询:"+student);
34         /**
35          * 第一次查询结束后关闭SqlSession
36          */
37         sqlSession.close();
38 
39         /**
40          * 为第二次查询建立新的SqlSession
41          */
42         sqlSession = factory.openSession(true);
43         studentDao = sqlSession.getMapper(StudentDao.class);
44         Student student1 = studentDao.queryById("00000000");
45         System.out.println("第二次查询:"+student1);
46         sqlSession.close();
47 
48     }
49 
50 }

  执行结果:

 1 [QC] DEBUG [main] org.apache.ibatis.cache.decorators.LoggingCache.getObject(60) | Cache Hit Ratio [com.dao.StudentDao]: 0.0
 2 [QC] DEBUG [main] org.apache.ibatis.transaction.jdbc.JdbcTransaction.openConnection(136) | Opening JDBC Connection
 3 [QC] DEBUG [main] org.apache.ibatis.datasource.pooled.PooledDataSource.popConnection(424) | Created connection 1138193439.
 4 [QC] DEBUG [main] org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(143) | ==>  Preparing: SELECT * FROM STUDENT T WHERE T.ID=? 
 5 [QC] DEBUG [main] org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(143) | ==> Parameters: 00000000(String)
 6 [QC] DEBUG [main] org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(143) | <==      Total: 1
 7 第一次查询:com.domain.Student@1442d7b5
 8 [QC] DEBUG [main] org.apache.ibatis.transaction.jdbc.JdbcTransaction.close(90) | Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@43d7741f]
 9 [QC] DEBUG [main] org.apache.ibatis.datasource.pooled.PooledDataSource.pushConnection(381) | Returned connection 1138193439 to pool.
10 [QC] DEBUG [main] org.apache.ibatis.cache.decorators.LoggingCache.getObject(60) | Cache Hit Ratio [com.dao.StudentDao]: 0.5
11 第二次查询:com.domain.Student@2bbaf4f0

 

参考文献:Mybatis的官方文档https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#cache

 

Mybatis的一级缓存和二级缓存

原文:https://www.cnblogs.com/baoqinghe/p/12609433.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!