一,查询缓存简介
mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。
mybaits提供一级缓存,和二级缓存。
一级缓存是SqlSession级别的缓存。在操作数据库时候,需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域是互相不影响的。
二级缓存是mapper级别的缓存,多个sqlSession去操作同一个mapper的sql语句,多个sqlSession可以共用二级缓存,二级缓存是跨sqlSession的。
二,一级缓存
1,一级缓存的原理
步骤:
1,第一次发起查询用户id为1的信息,先去缓存中查找是否存在id为1的信息,如果没有,发出sq语句,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。
2,如果sqlSession去执行commit操作(执行插入,更新,删除),清空sqlSession中的一级缓存,这样做的目的是为了让缓存中存储最新的信息,避免脏读。
3,当第二次查询用户id为1的信息的时候,先去缓存中查找是否有id为1的用户信息,缓存中有,则直接从缓存中取数据,不再发出sql查询语句。
2,一级缓存的测试
1,测试没有commit操作的时候,从缓存取得数据:
@Test public void testCache1() throws Exception { SqlSession sqlSession=sqlSessionFactory.openSession(); //创建代理对象 UserMapper userMapper=sqlSession.getMapper(UserMapper.class); //第一次发起请求,查询id为1的用户 User user1=userMapper.findUserById(1); System.out.println(user1.toString()); //第二次发起请求,查询id为1的用户 User user2=userMapper.findUserById(1); System.out.println(user2.toString()); sqlSession.close(); }
debug信息:
DEBUG [main] - Cache Hit Ratio [cn.itcast.mybatis.mapper.UserMapper]: 0.0 DEBUG [main] - Opening JDBC Connection DEBUG [main] - Created connection 819131219. DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@30d2f353] DEBUG [main] - ==> Preparing: SELECT * FROM user where id=? DEBUG [main] - ==> Parameters: 1(Integer) DEBUG [main] - <== Total: 1 cn.itcast.mybatis.po.User@68d9915 DEBUG [main] - Cache Hit Ratio [cn.itcast.mybatis.mapper.UserMapper]: 0.0 cn.itcast.mybatis.po.User@68d9915 DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@30d2f353] DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@30d2f353] DEBUG [main] - Returned connection 819131219 to pool.
第一次查询id=1,发出一条sql语句,之后在第二次查询的时候,没有任何sql语句。
2,测试存在commit操作的时候,缓存变化
@Test public void testCache1() throws Exception { SqlSession sqlSession=sqlSessionFactory.openSession(); //创建代理对象 UserMapper userMapper=sqlSession.getMapper(UserMapper.class); //第一次发起请求,查询id为1的用户 User user1=userMapper.findUserById(1); System.out.println(user1.toString()); /*如果sqlSession去执行commit操作,(插入,删除,更新),清空sqlSession的 一级缓存,这样做的目的是为了让缓存中存储的是最新信息,避免脏读 */ user1.setAddress("故宫"); userMapper.updateUser(user1); sqlSession.commit();//清空缓存 //第二次发起请求,查询id为1的用户 User user2=userMapper.findUserById(1); System.out.println(user2.toString()); sqlSession.close(); }
deubg信息:
DEBUG [main] - Checking to see if class cn.itcast.mybatis.mapper.UserMapperTest matches criteria [is assignable to Object] DEBUG [main] - Cache Hit Ratio [cn.itcast.mybatis.mapper.UserMapper]: 0.0 DEBUG [main] - Opening JDBC Connection DEBUG [main] - Created connection 538784332. DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@201d324c] DEBUG [main] - ==> Preparing: SELECT * FROM user where id=? DEBUG [main] - ==> Parameters: 1(Integer) DEBUG [main] - <== Total: 1 cn.itcast.mybatis.po.User@3eb6e46 DEBUG [main] - ==> Preparing: update user set username=?,birthday=?,sex=?,address=? where id=? DEBUG [main] - ==> Parameters: lhccccccccc(String), 2016-05-21 00:00:00.0(Timestamp), 1(String), 故宫(String), 1(Integer) DEBUG [main] - <== Updates: 1 DEBUG [main] - Committing JDBC Connection [com.mysql.jdbc.JDBC4Connection@201d324c] DEBUG [main] - Cache Hit Ratio [cn.itcast.mybatis.mapper.UserMapper]: 0.0 DEBUG [main] - ==> Preparing: SELECT * FROM user where id=? DEBUG [main] - ==> Parameters: 1(Integer) DEBUG [main] - <== Total: 1 cn.itcast.mybatis.po.User@4e30cbae DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@201d324c] DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@201d324c] DEBUG [main] - Returned connection 538784332 to pool.
在update语句执行之后,当再次查询id=1的时候,又发出sql语句。
3,一级缓存的应用
正式开发,是将mybatis和spring进行整合开发,事务控制在service中。
一个service方法中包括 很多mapper方法调用。
service{
//开始执行时,开启事务,创建SqlSession对象
//第一次调用mapper的方法findUserById(1)
//第二次调用mapper的方法findUserById(1),从一级缓存中取数据
//方法结束,sqlSession关闭
}
如果是执行两次service调用查询相同 的用户信息,不走一级缓存,因为session方法结束,sqlSession就关闭,一级缓存就清空。
三,二级缓存
1,原理
使用二级缓存步骤:
首先开启mybatis的二级缓存:
sqlSession1去查询id=1的用户信息,发出sql语句,查询到的结果会被放入二级缓存。
如果sqlSession3去执行相同mapper的sql,执行commit提交,清空该mapper下的二级缓存区域的数据。
sqlSession2区查询用户id为1的信息,去缓存中查找是否存在数据,如果存在则直接从缓存中取数据。
二级缓存与一级缓存区别:二级缓存范围更大,多个sqlSession可以共享一个UserMapper的二级缓存区域。UserMapper又一个二级缓存区域(按照namespace分),其他mapper也有自己的二级缓存区域(按照namespace分)。每一个namespace的mapper都有一个二级缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同 的二级缓存区域中。
2,开启二级缓存
开启了settings中的全局二级缓存配置后,如果某个mapper要开启二级缓存,需要在mapper的xml中加入:
<!-- 开启本mapper的namespace下的二级缓存 --> <cache/>
pojo类实现序列化接口:
(ps:为了将缓存数据取出执行反序列化操作,因为二级缓存数据存储介质多种多样,不一样在内存。)
3,测试代码
@Test public void testCache2() throws Exception { SqlSession sqlSession1=sqlSessionFactory.openSession(); SqlSession sqlSession2=sqlSessionFactory.openSession(); SqlSession sqlSession3=sqlSessionFactory.openSession(); //创建代理对象 UserMapper userMapper1=sqlSession1.getMapper(UserMapper.class); UserMapper userMapper2=sqlSession2.getMapper(UserMapper.class); UserMapper userMapper3=sqlSession2.getMapper(UserMapper.class); //第一次发起请求,查询id为1的用户 User user1=userMapper1.findUserById(1); System.out.println(user1.toString()); //这里执行关闭操作,将sqlSession中数据写入二级缓存中去 sqlSession1.close(); //第二次发起请求,查询id为1的用户 User user2=userMapper2.findUserById(1); System.out.println(user2.toString()); sqlSession2.close(); }
输出信息:
DEBUG [main] - Cache Hit Ratio [cn.itcast.mybatis.mapper.UserMapper]: 0.0 DEBUG [main] - Opening JDBC Connection DEBUG [main] - Created connection 1602614455. DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5f85f4b7] DEBUG [main] - ==> Preparing: SELECT * FROM user where id=? DEBUG [main] - ==> Parameters: 1(Integer) DEBUG [main] - <== Total: 1 cn.itcast.mybatis.po.User@1a6778eb DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5f85f4b7] DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@5f85f4b7] DEBUG [main] - Returned connection 1602614455 to pool. DEBUG [main] - Cache Hit Ratio [cn.itcast.mybatis.mapper.UserMapper]: 0.5 cn.itcast.mybatis.po.User@7b781fdd
只有第一次发出了sql语句,第二次并没有去查询。
加入带有commit操作的代码:
@Test public void testCache2() throws Exception { SqlSession sqlSession1=sqlSessionFactory.openSession(); SqlSession sqlSession2=sqlSessionFactory.openSession(); SqlSession sqlSession3=sqlSessionFactory.openSession(); //创建代理对象 UserMapper userMapper1=sqlSession1.getMapper(UserMapper.class); UserMapper userMapper2=sqlSession2.getMapper(UserMapper.class); UserMapper userMapper3=sqlSession2.getMapper(UserMapper.class); //第一次发起请求,查询id为1的用户 User user1=userMapper1.findUserById(1); System.out.println(user1.toString()); //这里执行关闭操作,将sqlSession中数据写入二级缓存中去 sqlSession1.close(); /*使用sqlSession3执行commit操作*/ User user=userMapper3.findUserById(1); user.setAddress("(>﹏<"); userMapper3.updateUser(user); //执行提交清空二级缓存 sqlSession3.commit(); sqlSession3.close(); //第二次发起请求,查询id为1的用户 User user2=userMapper2.findUserById(1); System.out.println(user2.toString()); sqlSession2.close(); }
输出信息:
DEBUG [main] - Cache Hit Ratio [cn.itcast.mybatis.mapper.UserMapper]: 0.0 DEBUG [main] - Opening JDBC Connection DEBUG [main] - Created connection 447056233. DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1aa58969] DEBUG [main] - ==> Preparing: SELECT * FROM user where id=? DEBUG [main] - ==> Parameters: 1(Integer) DEBUG [main] - <== Total: 1 cn.itcast.mybatis.po.User@c355f75 DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1aa58969] DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1aa58969] DEBUG [main] - Returned connection 447056233 to pool. DEBUG [main] - Cache Hit Ratio [cn.itcast.mybatis.mapper.UserMapper]: 0.5 DEBUG [main] - Opening JDBC Connection DEBUG [main] - Checked out connection 447056233 from pool. DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1aa58969] DEBUG [main] - ==> Preparing: update user set username=?,birthday=?,sex=?,address=? where id=? DEBUG [main] - ==> Parameters: lhccccccccc(String), 2016-05-21 00:00:00.0(Timestamp), 1(String), (>﹏<(String), 1(Integer) DEBUG [main] - <== Updates: 1 DEBUG [main] - ==> Preparing: SELECT * FROM user where id=? DEBUG [main] - ==> Parameters: 1(Integer) DEBUG [main] - <== Total: 1 cn.itcast.mybatis.po.User@320e64a7 DEBUG [main] - Rolling back JDBC Connection [com.mysql.jdbc.JDBC4Connection@1aa58969 DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1aa58969] DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1aa58969] DEBUG [main] - Returned connection 447056233 to pool.
代码中加入了需要commit的更改操作,二级缓存被更新,查询时候需要重新发出sql语句。
四,一些其他配置
1,禁用二级缓存
<select id="findUserById" parameterType="int" resultType="cn.itcast.mybatis.po.User" useCache="false"> SELECT * FROM user where id=#{value} </select>
在某个statement中设置usecache="false"会禁用当前二级缓存,即每次都会发出sql去查询新数据。默认情况是true.
2,刷新缓存(清空)
在mapper的同一个namespace中,如果有其它insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。
设置statement配置中的flushCache="true" 属性,默认情况下为true即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">
一般下执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存,这样可以避免数据库脏读。