MyBatis Review——查询缓存


一,查询缓存简介


        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,一级缓存的应用


   正式开发,是将mybatisspring进行整合开发,事务控制在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中,如果有其它insertupdatedelete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。


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


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

一般下执行完 commit 操作都需要刷新缓存, flushCache=true 表示刷新缓存,这样可以避免数据库脏读。








你可能感兴趣的:(mybatis,缓存)