MyBatis缓存介绍

https://www.cnblogs.com/xdp-gacl/p/4270403.html

 

只为成功找方法,不为失败找借口!

MyBatis学习总结(七)——Mybatis缓存

一、MyBatis缓存介绍

  正如大多数持久层框架一样,MyBatis 同样提供了一级缓存二级缓存的支持

  1. 一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Session,当 Session flush  close 之后,该Session中的所有 Cache 就将清空

  2. 二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。

  3. 对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear。

1.1、Mybatis一级缓存测试

复制代码

 1 package me.gacl.test;
 2 
 3 import me.gacl.domain.User;
 4 import me.gacl.util.MyBatisUtil;
 5 import org.apache.ibatis.session.SqlSession;
 6 import org.junit.Test;
 7 
 8 /**
 9  * @author gacl
10  * 测试一级缓存
11  */
12 public class TestOneLevelCache {
13     
14     /*
15      * 一级缓存: 也就Session级的缓存(默认开启)
16      */
17     @Test
18     public void testCache1() {
19         SqlSession session = MyBatisUtil.getSqlSession();
20         String statement = "me.gacl.mapping.userMapper.getUser";
21         User user = session.selectOne(statement, 1);
22         System.out.println(user);
23         
24         /*
25          * 一级缓存默认就会被使用
26          */
27         user = session.selectOne(statement, 1);
28         System.out.println(user);
29         session.close();
30         /*
31          1. 必须是同一个Session,如果session对象已经close()过了就不可能用了 
32          */
33         session = MyBatisUtil.getSqlSession();
34         user = session.selectOne(statement, 1);
35         System.out.println(user);
36         
37         /*
38          2. 查询条件是一样的
39          */
40         user = session.selectOne(statement, 2);
41         System.out.println(user);
42         
43         /*
44          3. 没有执行过session.clearCache()清理缓存
45          */
46         //session.clearCache(); 
47         user = session.selectOne(statement, 2);
48         System.out.println(user);
49         
50         /*
51          4. 没有执行过增删改的操作(这些操作都会清理缓存)
52          */
53         session.update("me.gacl.mapping.userMapper.updateUser",
54                 new User(2, "user", 23));
55         user = session.selectOne(statement, 2);
56         System.out.println(user);
57         
58     }
59 }

复制代码

1.2、Mybatis二级缓存测试

  1、开启二级缓存,在userMapper.xml文件中添加如下配置

<mapper namespace="me.gacl.mapping.userMapper">
<!-- 开启二级缓存 -->
<cache/>

  2、测试二级缓存

复制代码

 1 package me.gacl.test;
 2 
 3 import me.gacl.domain.User;
 4 import me.gacl.util.MyBatisUtil;
 5 import org.apache.ibatis.session.SqlSession;
 6 import org.apache.ibatis.session.SqlSessionFactory;
 7 import org.junit.Test;
 8 
 9 /**
10  * @author gacl
11  * 测试二级缓存
12  */
13 public class TestTwoLevelCache {
14     
15     /*
16      * 测试二级缓存
17      * 使用两个不同的SqlSession对象去执行相同查询条件的查询,第二次查询时不会再发送SQL语句,而是直接从缓存中取出数据
18      */
19     @Test
20     public void testCache2() {
21         String statement = "me.gacl.mapping.userMapper.getUser";
22         SqlSessionFactory factory = MyBatisUtil.getSqlSessionFactory();
23         //开启两个不同的SqlSession
24         SqlSession session1 = factory.openSession();
25         SqlSession session2 = factory.openSession();
26         //使用二级缓存时,User类必须实现一个Serializable接口===> User implements Serializable
27         User user = session1.selectOne(statement, 1);
28         session1.commit();//不懂为啥,这个地方一定要提交事务之后二级缓存才会起作用
29         System.out.println("user="+user);
30         
31         //由于使用的是两个不同的SqlSession对象,所以即使查询条件相同,一级缓存也不会开启使用
32         user = session2.selectOne(statement, 1);
33         //session2.commit();
34         System.out.println("user2="+user);
35     }
36 }

复制代码

1.3、二级缓存补充说明

  1. 映射语句文件中的所有select语句将会被缓存。

  2. 映射语句文件中的所有insert,update和delete语句会刷新缓存。

  3. 缓存会使用Least Recently Used(LRU,最近最少使用的)算法来收回。

  4. 缓存会根据指定的时间间隔来刷新。

  5. 缓存会存储1024个对象

cache标签常用属性:

<cache 
eviction="FIFO"  <!--回收策略为先进先出-->
flushInterval="60000" <!--自动刷新时间60s-->
size="512" <!--最多缓存512个引用对象-->
readOnly="true"/> <!--只读-->

 

分类: Mybatis

标签: MyBatis学习总结

好文要顶 关注我 收藏该文  

孤傲苍狼
关注 - 90
粉丝 - 14588

+加关注

17

0

« 上一篇:MyBatis学习总结(六)——调用存储过程
» 下一篇:MyBatis学习总结(八)——Mybatis3.x与Spring4.x整合

posted on 2015-02-03 22:26 孤傲苍狼 阅读(82405) 评论(30) 编辑 收藏

评论

#1楼 2015-04-14 12:35 干净的句号  

 

   
 

session1.commit();//不懂为啥,这个地方一定要提交事务之后二级缓存才会起作用

因为,二级缓存是从cache(mapper.xml中定义的cache)中取得,如果session不commit,那么,数据就不会放入cache中。所以,只有commit后,才能取得。

 
   

支持(13)反对(0)

  

 

#2楼 2015-08-14 15:09 梦幻之神magic  

 

   
 

写的很清楚

 
   

支持(1)反对(0)

  

 

#3楼 2015-08-24 17:28 jason_007  

 

   
 

赞!

 
   

支持(1)反对(0)

  

 

#4楼 2015-10-28 14:46 萧月宇轩  

 

   
 

 
   

支持(1)反对(0)

  

 

#5楼 2015-11-02 10:01 路遥天  

 

   
 

写的很清楚,对我有帮助,谢谢,推荐!

 
   

支持(0)反对(0)

  

 

#6楼 2015-11-02 10:01 路遥天  

 

   
 

@ 干净的句号
赞!

 
   

支持(0)反对(0)

  

 

#7楼 2015-11-02 10:38 youzhibing2904  

 

   
 

可以的

 
   

支持(0)反对(0)

  

 

#8楼 2015-11-09 15:47 首席  

 

   
 

您好,问下二级缓存测试,没感觉出来时二级缓存,各位怎么弄的

 
   

支持(0)反对(0)

  

 

#9楼 2015-12-15 09:53 fengyu_5  

 

   
 

如果对其中的部分select不需要缓存怎么配置呢?

 
   

支持(0)反对(0)

  

 

#10楼 2016-01-20 15:37 postnull  

 

   
 

@ 首席
其实应该打开log4j的trace日志,就可以看到效果了

 
   

支持(0)反对(0)

  

 

#11楼 2016-03-05 11:48 技术控少  

 

   
 

请问一下,一级缓存中第二个查询,我怎么知道它到底是从数据库中查到的还是从session缓存中查到的呢??

 
   

支持(0)反对(0)

  

 

#12楼 2016-03-09 15:22 Rang  

 

   
 

2. 映射语句文件中的所有insert,update和delete语句会刷新缓存。

dml语句不在同一个mapper中应该不会刷新文缓存的吧!?

 
   

支持(0)反对(0)

  

 

#13楼 2016-03-14 17:53 寂静风暴  

 

   
 

@ 技术控少
看执行语句需要的时间。。。。
执行前后分别获取个时间戳

 
   

支持(0)反对(0)

  

 

#14楼 2016-04-20 22:34 子非鱼220  

 

   
 

一级缓存,第一个查询不太理解,实际输出的时候ID为2的user已经被update了但是间隔的时间还是很短,是从一级缓存拿的,这不应该是有CUD操作后刷新缓存么

 
   

支持(0)反对(0)

  

 

#15楼 2016-04-24 17:38 码农甲  

 

   
 

@ 技术控少

引用请问一下,一级缓存中第二个查询,我怎么知道它到底是从数据库中查到的还是从session缓存中查到的呢??


你在第二个查询的地方设个断点,然后在数据库管理器中把数据给改了,然后再返回程序往下调试,如果session取到的数据和数据库修改后的数据不同,那么就是从session中缓存取的数据,否则就是从数据库中取到的数据了,
这是个比较笨的方法,10楼说的:其实应该打开log4j的trace日志,就可以看到效果了
这个方法我不会,悲催

 

 
   

支持(0)反对(0)

  

 

#16楼 2016-05-30 10:12 添仔哥哥  

 

   
 

@ 技术控少

引用请问一下,一级缓存中第二个查询,我怎么知道它到底是从数据库中查到的还是从session缓存中查到的呢??


你可以通过判断两次获取的user对象是否相同对象。

1

2

3

4

5

6

User user = session1.selectOne(statement, 1);

session1.commit();

System.out.println("user="+user);

User user2 = session2.selectOne(statement, 1);

System.out.println("user2="+user2);

System.out.println(user.equals(user2));

 

 
   

支持(3)反对(0)

  

 

#17楼 2016-06-07 15:23 Along丷  

 

   
 

为什么二级缓存出来的地址是不一样的?一样的才是从缓存中取出来的吧?

 
   

支持(0)反对(0)

  

 

#18楼 2016-08-25 16:29 ZDick  

 

   
 

factory.openSession(true);添加true 就不需要session.commit() 我认为是这样的,不知道说的对不对

 
   

支持(0)反对(1)

  

 

#19楼 2016-09-09 14:31 jiuson  

 

   
 

写得确实很好,通俗易懂

 
   

支持(0)反对(0)

  

 

#20楼 2016-09-18 10:45 Duang囧  

 

   
 

User.java那需要实体类实现可序列化接口public class User implements Serializable {...}
之前二级缓存测试报错org.apache.ibatis.cache.CacheException: Error serializing object. Cause: java.io.NotSerializableException: demo.User
给我这样的萌新一点经验

 
   

支持(0)反对(0)

  

 

#21楼 2016-11-04 11:17 依兰花  

 

   
 

有能把输出内容写一下的吗?为啥我感觉缓存没起啥作用呢?

 
   

支持(0)反对(0)

  

 

#22楼 2016-11-09 21:53 codingXiaxw  

 

   
 

@ 依兰花
看看这篇博客

 
   

支持(0)反对(0)

  

 

#23楼 2016-11-09 21:54 codingXiaxw  

 

   
 

@ 依兰花
它不让我输入链接,你自己在网址上打一下: codingxiaxw.cn

 
   

支持(0)反对(0)

  

 

#24楼 2016-11-17 23:00 rdm5158  

 

   
 

@ 首席
需要添加日志输出,并引用log4j-1.2.16.jar包

在src下新建log4j.properties文件,并添加以下内容:
log4j.rootLogger=debug,stdout,logfile
### 把日志信息输出到控制台 ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
#log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout

运行的时候,会输出SQL语句,以及缓存的命中率,如下:
DEBUG - Logging initialized using 'org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.
DEBUG - PooledDataSource forcefully closed/removed all connections.
DEBUG - PooledDataSource forcefully closed/removed all connections.
DEBUG - PooledDataSource forcefully closed/removed all connections.
DEBUG - PooledDataSource forcefully closed/removed all connections.
DEBUG - Cache Hit Ratio [me.gacl.demo10.mapping]: 0.0
DEBUG - Openning JDBC Connection
DEBUG - Created connection 280876206.
DEBUG - ooo Using Connection [oracle.jdbc.driver.T4CConnection@10bdd4ae]
DEBUG - ==> Preparing: select * from users where id=?
DEBUG - ==> Parameters: 21(Integer)
DEBUG - Cache Hit Ratio [me.gacl.demo10.mapping]: 0.5

查询了两次,但是只输出了一次sql语句,并且第二次的Cache Hit Ratio为0.5

 
   

支持(2)反对(0)

  

 

#25楼 2017-06-19 15:46 it小精灵  

 

   
 

看评论,大家都好强大!

 
   

支持(1)反对(0)

  

 

#26楼 2017-08-15 23:45 芋艿  

 

   
 

不错,收藏了

推荐下,分表分库数据库中间件 Sharding-JDBC 源码分析 17篇:http://www.yunai.me/categories/Sharding-JDBC/?cnblogs&602

 
   

支持(0)反对(0)

  

 

#27楼 2017-10-11 11:39 生如夏花般绚烂  

 

   
 

@ ZDick
亲测不可行

SqlSession session = sqlSessionFactory.openSession(true);
SqlSession session1 = sqlSessionFactory.openSession(true);
UserMapper userMapper = session.getMapper(UserMapper.class);
UserMapper userMapper2 = session1.getMapper(UserMapper.class);
User user1 = userMapper.findUserById(1);
System.out.println(user1);
User user2 = userMapper2.findUserById(1);
System.out.println(user2);

打印日志为
DEBUG [main] - Cache Hit Ratio [com.ssj.mapper.UserMapper]: 0.0
DEBUG [main] - Opening JDBC Connection
Wed Oct 11 11:36:15 GMT+08:00 2017 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
DEBUG [main] - Created connection 1157058691.
DEBUG [main] - ==> Preparing: select * from user where id=? 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
User [id=1, username=21ji, birthday=Thu Jul 10 00:00:00 GMT+08:00 2014, sex=2, address=null, orders=[]]

DEBUG [main] - Cache Hit Ratio [com.ssj.mapper.UserMapper]: 0.0
DEBUG [main] - Opening JDBC Connection
Wed Oct 11 11:36:15 GMT+08:00 2017 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
DEBUG [main] - Created connection 2007331442.
DEBUG [main] - ==> Preparing: select * from user where id=? 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
User [id=1, username=21ji, birthday=Thu Jul 10 00:00:00 GMT+08:00 2014, sex=2, address=null, orders=[]]

可见第二个缓冲命中率为0.0 并执行了sql语句

 
   

支持(0)反对(0)

  

 

#28楼 2018-03-12 09:13 hunterbird  

 

   
 

鲁丝门:
写个单态的获取SqlSessionFactory就可以了,楼主那个每次获得不同的factory不行。
单态工厂类:
public class SingleSessionFactory {

private static InputStream is;
private SingleSessionFactory(){
System.out.println("Singleton has loaded");
};
static{
String resource = "conf.xml";
is = Test1.class.getClassLoader().getResourceAsStream(resource);
}
private static SqlSessionFactory sessionFactoryInstance = new SqlSessionFactoryBuilder().build(is,"development");
public static SqlSessionFactory getSessionFactoryInstance(){return sessionFactoryInstance;};

}
}
测试类:
SqlSession session1 = SingleSessionFactory.getSessionFactoryInstance().openSession();
SqlSession session2 = SingleSessionFactory.getSessionFactoryInstance().openSession();
User user = session1.selectOne(statement, 1);
session1.commit();
System.out.println("user="+user);


user = session2.selectOne(statement, 1);
session2.commit();
System.out.println("user2="+user);
输出:
2018-03-12 09:12:25,280 DEBUG [main] JdbcTransaction - Openning JDBC Connection
2018-03-12 09:12:25,483 DEBUG [main] PooledDataSource - Created connection 1562537029.
2018-03-12 09:12:25,483 DEBUG [main] BaseJdbcLogger - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@5d226c45]
2018-03-12 09:12:25,483 DEBUG [main] BaseJdbcLogger - ==> Preparing: select * from users where id=? 
2018-03-12 09:12:25,530 DEBUG [main] BaseJdbcLogger - ==> Parameters: 1(Integer)
user=User [id=1, name=孤傲苍狼, age=27]
2018-03-12 09:12:25,580 DEBUG [main] LoggingCache - Cache Hit Ratio [com.cov.mapping.userMapper]: 0.5
user2=User [id=1, name=孤傲苍狼, age=27]。

噢耶,鲁定!

 
   

支持(1)反对(0)

  

 

#29楼 2018-04-20 14:45 慢走夜街  

 

   
 

真的学到了

 
   

支持(0)反对(0)

  

 

#30楼 2018-08-08 18:36 激励的11月份  

 

   
 

@ hunterbird
在一级缓存中session必须是同一个session

 
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值