博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Mybatis学习(6)动态加载、一二级缓存
阅读量:5732 次
发布时间:2019-06-18

本文共 12234 字,大约阅读时间需要 40 分钟。

一、动态加载:

resultMap可以实现高级映射(使用association、collection实现一对一及一对多映射),association、collection具备延迟加载功能。

需求:

如果查询订单并且关联查询用户信息。如果先查询订单信息即可满足要求,当需要查询用户信息时再查询用户信息。把对用户信息的按需去查询就是延迟加载。

需要先说明下是按照这个sql的思路来实现延迟加载的:

mysql> select orders.*, (select user.username from user where orders.user_id = user.id) username from orders;
(对于这种查询,可以分成两部来理解,首先忽略整个select子查询,查出订单表中的数据,然后根据订单表的user_id执行子查询,对于一个orders的user_id,子查询只能返回一条数据,如果子查询返回多条数据则会出错,另外,每一条select子查询只能查询一个字段。)
 
mapper.xml:

这边需要配置settings  延迟加载:

mapper接口:

//查询订单关联查询用户,用户信息是延迟加载public List
findOrdersLazyLoadingUser() throws Exception;

测试代码:

// 查询订单关联查询用户,用户信息使用延迟加载    @Test    public void FindOrdersLazyLoadingUser() throws Exception {        SqlSession sqlSession = sqlSessionFactory.openSession();        OrdersMapperCustom ordersMapperCustom = sqlSession.getMapper(OrdersMapperCustom.class);        // 查询订单信息(单表)        List
orders = ordersMapperCustom.findOrdersLazyLoadingUser(); for(Orders order: orders){ // 执行getUser()去查询用户信息,这里实现按需加载 User user = order.getUser(); System.out.println(user); } sqlSession.close(); }
View Code

可以看到console输出的打印语句:

DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@20e6423f]

DEBUG [main] - ==> Preparing: select * from orders;
DEBUG [main] - ==> Parameters:
DEBUG [main] - <== Total: 3
DEBUG [main] - ==> Preparing: SELECT * FROM USER WHERE id=?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
------->> User [id=1, username=王五, sex=2, birthday=null, address=null]
------->> User [id=1, username=王五, sex=2, birthday=null, address=null]
DEBUG [main] - ==> Preparing: SELECT * FROM USER WHERE id=?
DEBUG [main] - ==> Parameters: 10(Integer)
DEBUG [main] - <== Total: 1
------->> User [id=10, username=张三, sex=1, birthday=Thu Jul 10 00:00:00 CST 2014, address=北京市]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@20e6423f]

 

 二、1级缓存、2级缓存:

mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。

一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。

二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

 

1.一级缓存:

工作原理:

第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。

如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。

第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。

 

测试代码:

一级缓存不需要在配置文件中配置,mybatis默认支持一级缓存;

1 // 一级缓存测试 2     @Test 3     public void testCache1() throws Exception { 4         SqlSession sqlSession = sqlSessionFactory.openSession(); 5         UserMapper userMapper = sqlSession.getMapper(UserMapper.class); 6         // 第一次发起请求,查询id为1的用户 7         User user1 = userMapper.findUserById(1); 8         System.out.println(user1); 9 10         // 如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。11         // 更新user1的信息12         // user1.setUsername("测试用户22");13         // userMapper.updateUser(user1);14         // //执行commit操作去清空缓存15         // sqlSession.commit();16 17         // 第二次发起请求,查询id为1的用户18         User user2 = userMapper.findUserById(1);19         System.out.println(user2);20         21         sqlSession.close();22     }
一级缓存测试

 

 

2.二级缓存:

原理:

mybatis全局配置settings中默认开始二级缓存;

sqlSession1去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到二级缓存中。

如果SqlSession3去执行相同 mapper下sql,执行commit提交,清空该 mapper下的二级缓存区域的数据。

sqlSession2去查询用户id为1的用户信息,去缓存中找是否存在数据,如果存在直接从缓存中取出数据。

二级缓存与一级缓存区别:

二级缓存的范围更大,多个sqlSession可以共享一个UserMapper的二级缓存区域。

二级缓存区域(按namespace分)。两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同 的二级缓存区域中。

配置:

1)开启mybatis的二级缓存:

2)在mapper.xml中开启二级缓存:

....

3) pojo类实现序列化接口:

为了将缓存数据取出执行反序列化操作,因为二级缓存数据存储介质多种多样,不一定在内存。

public class User implements Serializable{    //属性名和数据库表的字段对应        private int id;        private String username;// 用户姓名        private String sex;// 性别        private Date birthday;// 生日        private String address;// 地址        .....}
View Code

测试代码:

1.先单纯的测试跨sqlSession级别的二级缓存,查看发出的sql语句:

1     // 二级缓存测试 2     @Test 3     public void testCache2() throws Exception { 4         SqlSession sqlSession1 = sqlSessionFactory.openSession(); 5         SqlSession sqlSession2 = sqlSessionFactory.openSession(); 6          7         UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class); 8         // 第一次发起请求,查询id为1的用户 9         User user1 = userMapper1.findUserById(1);10         System.out.println(user1);11         //这里执行关闭操作,将sqlsession中的数据写到二级缓存区域12         sqlSession1.close();13     14         UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);15         // 第二次发起请求,查询id为1的用户16         User user2 = userMapper2.findUserById(1);17         System.out.println(user2);18         sqlSession2.close();19     }
View Code

看console:

DEBUG [main] - Cache Hit Ratio [com.cy.mapper.UserMapper]: 0.0

DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 305414881.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@123442e1]
DEBUG [main] - ==> Preparing: SELECT * FROM USER WHERE id=?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
------->> User [id=1, username=王五, sex=2, birthday=null, address=null]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@123442e1]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@123442e1]
DEBUG [main] - Returned connection 305414881 to pool.
DEBUG [main] - Cache Hit Ratio [com.cy.mapper.UserMapper]: 0.5
------->> User [id=1, username=王五, sex=2, birthday=null, address=null]

2.sqlSession3的commit操作,清空二级缓存,再看发出的sql语句:

// 二级缓存测试    @Test    public void testCache2() throws Exception {        SqlSession sqlSession1 = sqlSessionFactory.openSession();        SqlSession sqlSession2 = sqlSessionFactory.openSession();        SqlSession sqlSession3 = sqlSessionFactory.openSession();                UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);        // 第一次发起请求,查询id为1的用户        User user1 = userMapper1.findUserById(1);        System.out.println(user1);        //这里执行关闭操作,将sqlsession中的数据写到二级缓存区域        sqlSession1.close();            //使用sqlSession3执行commit()操作        UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);        User user  = userMapper3.findUserById(1);        user.setUsername("张明明");        userMapper3.updateUser(user);        //执行提交,清空UserMapper下边的二级缓存        sqlSession3.commit();        sqlSession3.close();                UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);        // 第二次发起请求,查询id为1的用户        User user2 = userMapper2.findUserById(1);        System.out.println(user2);        sqlSession2.close();    }
View Code

看console:

DEBUG [main] - Cache Hit Ratio [com.cy.mapper.UserMapper]: 0.0DEBUG [main] - Opening JDBC ConnectionDEBUG [main] - Created connection 1795834361.DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@6b0a41f9]DEBUG [main] - ==>  Preparing: SELECT * FROM USER WHERE id=? DEBUG [main] - ==> Parameters: 1(Integer)DEBUG [main] - <==      Total: 1------->> User [id=1, username=王五, sex=2, birthday=null, address=null]DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@6b0a41f9]DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@6b0a41f9]DEBUG [main] - Returned connection 1795834361 to pool.DEBUG [main] - Cache Hit Ratio [com.cy.mapper.UserMapper]: 0.5DEBUG [main] - Opening JDBC ConnectionDEBUG [main] - Checked out connection 1795834361 from pool.DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@6b0a41f9]DEBUG [main] - ==>  Preparing: update user set username=?,birthday=?,sex=?,address=? where id=? DEBUG [main] - ==> Parameters: 张明明(String), null, 2(String), null, 1(Integer)DEBUG [main] - <==    Updates: 1DEBUG [main] - Committing JDBC Connection [com.mysql.jdbc.JDBC4Connection@6b0a41f9]DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@6b0a41f9]DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@6b0a41f9]DEBUG [main] - Returned connection 1795834361 to pool.DEBUG [main] - Cache Hit Ratio [com.cy.mapper.UserMapper]: 0.3333333333333333DEBUG [main] - Opening JDBC ConnectionDEBUG [main] - Checked out connection 1795834361 from pool.DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@6b0a41f9]DEBUG [main] - ==>  Preparing: SELECT * FROM USER WHERE id=? DEBUG [main] - ==> Parameters: 1(Integer)DEBUG [main] - <==      Total: 1------->> User [id=1, username=张明明, sex=2, birthday=null, address=null]
View Code

 

关于二级缓存的一些配置参数:

1.useCache配置:

在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true

<select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">

2.flushCache清空缓存:--一般执行完commit都需要刷新缓存(清空),这个默认true就行。

在mapper的同一个namespace中,如果有其它insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。

设置statement配置中的flushCache="true" 属性,默认情况下为true即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。

<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">

 

 

三、mybatis整合ehcache:

ehcache是一个分布式缓存框架。

系统为了提高系统并发,性能、一般对系统进行分布式部署(集群部署方式)

分布式缓存原理:

不使用分布缓存,缓存的数据在各各服务单独存储,不方便系统 开发。所以要使用分布式缓存对缓存数据进行集中管理。

mybatis无法实现分布式缓存,需要和其它分布式缓存框架进行整合。

整合过程:

1)mybatis提供了cache接口,如果要实现自己的缓存逻辑,实现cache接口即可(mybatis自己实现的cache:public class PerpetualCache implements Cache )

加入ehcache包:ehcache-core.jar 和mybatis-ehcache.jar:

2)配置mapper.xml,使用ehcache的实现cache的实现类:

1 
2 3
7
8 ....9

3)加入ehcache的配置文件

 在config source目录下加入ehcache.xml:

1 
3
4
13
14
View Code

测试代码:

// 二级缓存测试    @Test    public void testCache2() throws Exception {        SqlSession sqlSession1 = sqlSessionFactory.openSession();        SqlSession sqlSession2 = sqlSessionFactory.openSession();                UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);        User user1 = userMapper1.findUserById(1);        System.out.println(user1);        sqlSession1.close();                UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);        User user2 = userMapper2.findUserById(1);        System.out.println(user2);        sqlSession2.close();    }
View Code

看console打印,使用了缓存:

DEBUG [main] - Cache Hit Ratio [com.cy.mapper.UserMapper]: 0.0DEBUG [main] - Opening JDBC ConnectionDEBUG [main] - Created connection 1909716601.DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@71d3f679]DEBUG [main] - ==>  Preparing: SELECT * FROM USER WHERE id=? DEBUG [main] - ==> Parameters: 1(Integer)DEBUG [main] - <==      Total: 1------->> User [id=1, username=张明明, sex=2, birthday=null, address=null]DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@71d3f679]DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@71d3f679]DEBUG [main] - Returned connection 1909716601 to pool.DEBUG [main] - Cache Hit Ratio [com.cy.mapper.UserMapper]: 0.5------->> User [id=1, username=张明明, sex=2, birthday=null, address=null]
View Code

 

 

四、mybatis二级缓存的应用场景和局限性:

应用场景:

对于访问多的查询请求且用户对查询结果实时性要求不高,此时可采用mybatis二级缓存技术降低数据库访问量,提高访问速度,业务场景比如:耗时较高的统计分析sql、电话账单查询sql等。

实现方法如下:通过设置刷新间隔时间,由mybatis每隔一段时间自动清空缓存,根据数据变化频率设置缓存刷新间隔flushInterval,比如设置为30分钟、60分钟、24小时等,根据需求而定。

 

局限性:

mybatis二级缓存对细粒度的数据级别的缓存实现不好,比如如下需求:对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存就无法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息,因为mybaits的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空。解决此类问题需要在业务层根据需求对数据有针对性缓存。

转载于:https://www.cnblogs.com/tenWood/p/6287832.html

你可能感兴趣的文章
网游内存数据库的设计(2)
查看>>
awk学习杂记
查看>>
Linux系统的命令别名功能(转)
查看>>
vs2010在进行数据架构比较时报'text lines should not be null'错误
查看>>
实验楼学习linux第一章第四节linux目录结构及文件基本操作
查看>>
BZOJ1853:[SCOI2010]幸运数字 & BZOJ2393:Cirno的完美算数教室——题解
查看>>
Eclipse打开时“发现了以元素'd:skin'”开头的无效内容。此处不应含有子元素的解决方法...
查看>>
C#Contains方法的错误理解
查看>>
python实战===一行代码就能搞定的事情!
查看>>
linux卸载一个源码包安装的软件的流程
查看>>
python 基础复习 13
查看>>
该公众号暂时无法提供服务请稍后再试
查看>>
Android自定义组件系列【8】——遮罩文字动画
查看>>
使用OpenCV读取摄像头图像并显示
查看>>
Flask 上下文管理
查看>>
堆排序
查看>>
C++中const用法总结
查看>>
Java基础加强-代理
查看>>
Arduino和C51开发光敏传感器
查看>>
java_泛型方法使用实例
查看>>