zoukankan      html  css  js  c++  java
  • mybatis 使用缓存策略

    mybatis中默认开启缓存

      1、mybatis中,默认是开启缓存的,缓存的是一个statement对象。

    不同情况下是否会使用缓存

      同一个SqlSession对象,重复调用同一个id的<select>(id必须相同)的时候,缓存才会生效,两者缺一不可,而是会执行两次sql,并不会使用缓存,因为一个SqlSession就是一个数据库连接。

      下面举一个例子,首先看一个PersonMapper.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
      PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
      "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <mapper namespace="lixin.gan.mapper.PersonMapper">
    	<select id="selectAll1"  resultType="Person">
    		select * from person
    	</select>
    	
    	<select id="selectAll2"  resultType="Person">
    		select * from person
    	</select>
    </mapper>
    

      针对上面这一个xml来进行测试

      不同SqlSession调用同一个id的方法

    public class Test {
    	public static void main(String[] args) throws IOException {		
    		InputStream config = Resources.getResourceAsStream("mybatis.xml");
    		SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(config);
    		
    		SqlSession session1 = factory.openSession();
    		PersonMapper personMapper1 = session1.getMapper(PersonMapper.class);
    		personMapper1.selectAll1();
    
    		SqlSession session2 = factory.openSession();
    		PersonMapper personMapper2 = session2.getMapper(PersonMapper.class);
    		personMapper2.selectAll1();
    	}
    }
    

      上面这个代码中,创建了两个不同的sqlsession(两个数据库连接),虽然调用的是同一个id对应的方法,但是其实sql语句是执行了两次,利用log4j打印的日志如下:

    org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==>  Preparing: select * from person 
    org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==> Parameters: 
    org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - <==      Total: 4
    org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==>  Preparing: select * from person 
    org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==> Parameters: 
    org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - <==      Total: 4
    

      

      同一个SqlSession调用不同id,但是sql相同的方法 

    public class Test {
    	public static void main(String[] args) throws IOException {
    				
    		InputStream config = Resources.getResourceAsStream("mybatis.xml");
    		SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(config);
    		
    		SqlSession session1 = factory.openSession();
    		PersonMapper personMapper1 = session1.getMapper(PersonMapper.class);
    		personMapper1.selectAll1();
    		personMapper1.selectAll2();
    	}
    }
    

      上面这个代码,虽然只创建了一个SqlSession,使用这个SqlSession去调用了两个方法,这两个方法的id不同,一个是selectAll1,另一个是selectAll2,即使两个方法里面的sql相同,也是不会使用缓存的,所以sql语句仍然会执行了两次,利用log4j打印的日志依旧依旧是:

    org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==>  Preparing: select * from person 
    org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==> Parameters: 
    org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - <==      Total: 4
    org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==>  Preparing: select * from person 
    org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==> Parameters: 
    org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - <==      Total: 4
    

      

      同一个SqlSession,调用同一个id的方法。

    public class Test {
    	public static void main(String[] args) throws IOException {
    				
    		InputStream config = Resources.getResourceAsStream("mybatis.xml");
    		SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(config);
    		
    		SqlSession session1 = factory.openSession();
    		PersonMapper personMapper1 = session1.getMapper(PersonMapper.class);
    		personMapper1.selectAll1();
    		personMapper1.selectAll1();
    	}
    }
    

      虽然上面这段代码,进了两次selectAll1()操作,但是因为是一个SqlSession,并且方法id相同,所以,会使用缓存,使用log4j打印的日志显示,只进行了一次查询:

    org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==>  Preparing: select * from person 
    org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==> Parameters: 
    org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - <==      Total: 4
    

      

    一级缓存与二级缓存

      前面已经看到了,使用缓存的两个必要条件:1、同一个SqlSession;2、调用同一个id的方法。

      但是有时候,情况是这样的,多个用户请求某个数据,其实执行的sql都是一样的,但是因为是不同用户,所以,就会产生多个SqlSession,多个SqlSession调用同一个id的方法,仍然不会使用缓存。

      这个时候,其实是存在资源浪费的问题,既然他们需要的数据是一样的,那么何必再重复查询一次呢,如果数据量很大,重复查询一次,岂不是耗费更多的资源?

      mybatis提供了解决办法---二级缓存。方法就是:修改mapper文件,增加cache标签。

      前面说到的缓存其实一级缓存,另外还有二级缓存:

      1、一级缓存是SqlSession缓存,缓存的数据存在每一个SqlSession各自的空间中。

      2、二级缓存是SqlSessionFactory缓存,缓存的数据存在每一个SqlSessionFactory各自的空间中。

    一级缓存与二级缓存的作用范围

      1、一级缓存的作用范围是同一个SqlSession,并且只有当调用的方法id相同,才可以使用本SqlSession中的缓存。

      2、二级缓存的作用范围是由同一个SqlSessionFactory对象创建出的所有SqlSession,不同的SqlSessionFactory创建的SqlSession不能共享二级缓存。

    一级缓存与二级缓存的原理

      当一个SqlSession要查数据,会先查看一级缓存中是不是存在缓存,如果存在缓存,则直接从当前SqlSession的一级缓存中读取数据。如果不存在缓存,那么就会执行sql命令,去数据库中查询数据,并且将查询出的数据,保存到当前SqlSession对应的一级缓存中。可以调用clearCache()来清除当前SqlSession的一级缓存数据。

      各个SqlSession的一级缓存是不能共享的。但是二级缓存是可以共享的,于是乎,可以将一级缓存中的数据,放到二级缓存中,然后其他SqlSession在查询之前,首先到一级缓存中查看是否有数据,如果一级缓存有数据,就从一级缓存中取数据;如果一级缓存中没有数据,如果在mapper.xml中设置了cache,那么可以继续查询二级缓存,如果二级缓存中存在要查询到数据,那么就从二级缓存中读取数据,否则就从数据库中读取数据。

    使用二级缓存的前提

      二级缓存共享数据的一个前提是:一个SqlSession调用close()之后,才会将一级缓存中的数据放到二级缓存中,如果不关闭SqlSession,数据只会存在于一级缓存中,数据不会发生共享。

      这个前提很重要!

    什么时候使用二级缓存

      当某些数据频繁被使用,并且很少被修改的情况。

      如果数据频繁修改,一定不要放到二级缓存中。

    测试使用二级缓存

      修改后的PersonMapper.xml如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
      PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
      "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <mapper namespace="lixin.gan.mapper.PersonMapper">
    	<cache readOnly="true"></cache>
    	<select id="selectAll1"  resultType="Person">
    		select * from person
    	</select>
    	
    	<select id="selectAll2"  resultType="Person">
    		select * from person
    	</select>
    </mapper>
    

      正确、正常的测试代码

    public class Test {
    	public static void main(String[] args) throws IOException {
    				
    		InputStream config = Resources.getResourceAsStream("mybatis.xml");
    		SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(config);
    		
    		SqlSession session1 = factory.openSession();
    		PersonMapper personMapper1 = session1.getMapper(PersonMapper.class);
    		personMapper1.selectAll1();
    		
    		// 可以调用clearCache来清空当前SqlSession的缓存
    		//session1.clearCache();
    		
    		// 需要将session关闭之后,才会将数据放进二级缓存,否则数据不会放进二级缓存
    		session1.close();
    		
    		SqlSession session2 = factory.openSession();
    		PersonMapper personMapper2 = session2.getMapper(PersonMapper.class);
    		personMapper2.selectAll1();
    		
    	}
    }
    

      log4j记录的日志如下:

    org.apache.ibatis.cache.decorators.LoggingCache - line:62 - Cache Hit Ratio [lixin.gan.mapper.PersonMapper]: 0.0
    org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==>  Preparing: select * from person 
    org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==> Parameters: 
    org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - <==      Total: 4
    org.apache.ibatis.cache.decorators.LoggingCache - line:62 - Cache Hit Ratio [lixin.gan.mapper.PersonMapper]: 0.5
    

      

      不关闭SqlSession,没有发生共享的测试

    public class Test {
    	public static void main(String[] args) throws IOException {
    				
    		InputStream config = Resources.getResourceAsStream("mybatis.xml");
    		SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(config);
    		
    		SqlSession session1 = factory.openSession();
    		PersonMapper personMapper1 = session1.getMapper(PersonMapper.class);
    		personMapper1.selectAll1();
    		
    		// 可以调用clearCache来清空当前SqlSession的缓存
    		//session1.clearCache();
    		
    		// 需要将session关闭之后,才会将数据放进二级缓存,否则数据不会放进二级缓存
    		//session1.close();
    		// 此处没有关闭session1,所以数据不会放进二级缓存中
    		
    		SqlSession session2 = factory.openSession();
    		PersonMapper personMapper2 = session2.getMapper(PersonMapper.class);
    		personMapper2.selectAll1();
    		
    	}
    }
    

      log4j打印的日志很清楚的记录了上面的代码,执行了两次sql,因为数据没有放进二级缓存,在session2在一级缓存和二级缓存中都没有发现缓存,所以也要执行一次sql。

    org.apache.ibatis.cache.decorators.LoggingCache - line:62 - Cache Hit Ratio [lixin.gan.mapper.PersonMapper]: 0.0
    org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==>  Preparing: select * from person 
    org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==> Parameters: 
    org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - <==      Total: 4
    org.apache.ibatis.cache.decorators.LoggingCache - line:62 - Cache Hit Ratio [lixin.gan.mapper.PersonMapper]: 0.0
    org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==>  Preparing: select * from person 
    org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==> Parameters: 
    org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - <==      Total: 4
    

      

  • 相关阅读:
    Spring框架的反序列化远程代码执行漏洞分析(转)
    JVM调优总结(转)
    创造与熟练与微创造
    一次 react-router 中遇到的小坑
    mac中强大的快捷键
    数组思维 -- join的一些用法感悟
    undefined null 与 字符串相加
    mobx中的数组需要注意的地方
    更换鼠标垫(鼠标)的心路历程
    Object.defineProperty 中的 writable 和 configurable 和 enumerable 的理解
  • 原文地址:https://www.cnblogs.com/-beyond/p/10130726.html
Copyright © 2011-2022 走看看