五mybatis两级缓存

Posted 啄木鸟伍迪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了五mybatis两级缓存相关的知识,希望对你有一定的参考价值。

  一级缓存:(本地缓存) sqlSession级别的缓存。

  •  一级缓存是一直开启的。
  •   与数据库同一次会话期间查询到的数据会放在本地缓存中。
  •   以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查数据库。

验证实例(实时使用上一篇的目录结构):

接口文件EmployeeMapper.java:

Employee getEmpById(int id);

映射文件EmployeeMapper.xml

 <select id="getEmpById" parameterType="Integer" resultType="entity.Employee">
         select LAST_NAME AS
         lastName,gender as gender,email as email from
         tbl_employee where id =#asdsdfsdf
     </select>

junit测试类:

 @Test
     public void test02() 
         String resource = "mybatis-config.xml";
         SqlSession openSession = null;
         try 
             InputStream inputStream = Resources.getResourceAsStream(resource);
             SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
             // 获取openSession 不会自动提交数据
             openSession = sqlSessionFactory.openSession(true);
             EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
             Employee employee = mapper.getEmpById(1);
             Employee employee1 = mapper.getEmpById(1);
             System.out.println("测试一级缓存:" + employee);
             System.out.println("测试一级缓存:" + employee1 + "," + (employee == employee1));
          catch (Exception e) 
             // TODO: handle exception
          finally 
             if (openSession != null) 
                 openSession.close();
             
 
         
     

运行结果:

测试一级缓存:Employee [id=null, lastName=joy33333, email=joy52112225@iclound.com, gender=女]

测试一级缓存:Employee [id=null, lastName=joy33333, email=joy52112225@iclound.com, gender=女],true

运行结果说明:

employee ==employee1 结果为true说明两个地址值相同,说明是同一个employee;
控制台日志显示查询的SQL语句只执行了一次,说明SQL只执行了一次;

 

 一级缓存失效的情况(没有使用到当前一级缓存的情况:效果就是,还需要像数据库发请求)

  1.只要sqlSession不同

  2.sqlSession相同,查询条件不同;原因是当前一级缓存中还没有该数据;

  3.sqlSession相同,两次查询中执行了 增删改 操作也会失效;原因是增删改操作可能会影响数据。

  4.qlSession相同 但是手动清空了一级缓存  

验证实例(实时使用上一篇的目录结构):

接口文件EmployeeMapper.java:

 同上

映射文件EmployeeMapper.xml

 同上

junit测试类:

 @Test
     public void test02() 
         String resource = "mybatis-config.xml";
         SqlSession openSession = null;
         SqlSession openSession2 = null;
         try 
             InputStream inputStream = Resources.getResourceAsStream(resource);
             SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
             // 获取openSession 不会自动提交数据
             openSession = sqlSessionFactory.openSession(true);
             openSession2 = sqlSessionFactory.openSession(true);
             EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
             Employee employee = mapper.getEmpById(1);
             Employee employee1 = mapper.getEmpById(1);
             System.out.println("测试一级缓存:" + employee);
             System.out.println("测试一级缓存:" + employee1 + "," + (employee == employee1));
 
             EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
             Employee employee2 = mapper2.getEmpById(1);
             System.out.println("sqlSession改变 一级缓存失效:" + employee2 + "," + (employee1 == employee2));
             EmployeeMapper mapper23 = openSession2.getMapper(EmployeeMapper.class);
             Employee employee23 = mapper23.getEmpById(2);
             System.out.println("查询条件改变改变 一级缓存失效:" + employee23 + "," + (employee1 == employee23));
 
             Employee emp = new Employee();
             emp.setLastName("Tom");
             emp.setGender("男");
             emp.setEmail("678@qq.com");
             mapper23.addEmp(emp);
             Employee employee4 = mapper23.getEmpById(2);
             System.out.println("查询条件相同+sqlSession相同 一级缓存失效:" + employee23 + "," + (employee4 == employee23));
 
             Employee employee5 = mapper23.getEmpById(2);
             openSession2.clearCache();
             Employee employee6 = mapper23.getEmpById(2);
             System.out.println("手动清楚缓存:" + (employee5 == employee6));
          catch (Exception e) 
             // TODO: handle exception
          finally 
             if (openSession != null) 
                 openSession.close();
             
 
         
     

运行结果:如图:

 

 

  二级缓存:(全局缓存) 基于namespace级别的一个缓存:

 一个namespace对应一个二级缓存;

  工作机制:

  1.一个会话,查询一条数据,这个数据会被放在一级缓存中;

  2.如果会话关闭,一级缓存中的数据会被保存到二级缓存中;新会话查询信息,就可以参照二级缓存中的内容

  3.sqlSession EmployeeMapper Employee DepartmentMapper Department

  不同namespace查出的数据会放在自己对应的缓存(map)中

  效果,数据会从二级缓存中取,查出的数据都会放在一级缓存中; 只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中;

   

  使用步骤:

  1.开启全局二级缓存配置:<setting name="cacheEnabled" value="true"/>

  2.去mapper.xml中配置使用二级缓存;

  3.我们的POJO需要实现序列化接口;

验证实例(实时使用上一篇的目录结构):

 全局文件mybatis-config.xml

 <?xml version="1.0" encoding="UTF-8" ?>
 <!DOCTYPE configuration
   PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
   "http://mybatis.org/dtd/mybatis-3-config.dtd">
 <configuration>
     <properties resource="dbconfig.properties"></properties>
 
     <settings>
         <setting name="cacheEnabled" value="true" />
     </settings>
 
     <environments default="development">
         <environment id="development">
             <transactionManager type="JDBC" />
             <dataSource type="POOLED">
                 <property name="driver" value="$jdbc.driver" />
                 <property name="url" value="$jdbc.url" />
                 <property name="username" value="$jdbc.username" />
                 <property name="password" value="$jdbc.password" />
             </dataSource>
         </environment>
         <environment id="development_mysql2">
             <transactionManager type="JDBC" />
             <dataSource type="POOLED">
                 <property name="driver" value="$jdbc2.driver" />
                 <property name="url" value="$jdbc2.url" />
                 <property name="username" value="$jdbc2.username" />
                 <property name="password" value="$jdbc2.password" />
             </dataSource>
         </environment>
     </environments>
 
     <databaseIdProvider type="DB_VENDOR">
         <!-- 为不同的数据库厂商取别名 -->
         <property name="MySQL" value="mysql" />
         <property name="Oracle" value="oracle" />
         <property name="SQL Server" value="sqlserver" />
     </databaseIdProvider>
     <mappers>
         <package name="dao" />
     </mappers>
 </configuration>

映射文件EmployeeMapper.xml:使用二级缓存<cache></cache>

cache 标签的属性说明:

  • eviction:缓存回收策略;
  • flushInterval:缓存刷新间隔,多长时间清空一次,默认不清空
  • readOnly:缓存只读;true: mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据;
  • mybatis为了加快速度,直接把数据在缓存中的引用交给用户;不安全;
  • false:非只读;mybatis局的数据可能会被修改。mybatis会利用序列化和反序列化的技术克隆一份新数据给用户;安全,速度慢
  • size:存放多少元素;
  • type:指定自定义缓存的全类名;实现cache接口即可;
 <?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="dao.EmployeeMapper">
 <cache></cache>
     <insert id="addEmp" parameterType="entity.Employee" useGeneratedKeys="true" keyProperty="id">
         insert into tbl_employee(last_name,gender,email) values(
         #lastName,#gender,#email
         )
     </insert>
     
     <select id="getEmpById" parameterType="Integer" resultType="entity.Employee">
         select LAST_NAME AS
         lastName,gender as gender,email as email from
         tbl_employee where id =#asdsdfsdf
     </select>
 
 </mapper>

Employee.java实体类实现序列化接口:

 package entity;
 
 import java.io.Serializable;
 
 public class Employee implements Serializable 
 
     /**
      * 
      */
     private static final long serialVersionUID = -6962919367201266002L;
     private Integer id;
     private String lastName;
     private String email;
     private String gender;
     private Department department;
 
     public Integer getId() 
         return id;
     
 
     public void setId(Integer id) 
         this.id = id;
     
 
     public String getLastName() 
         return lastName;
     
 
     public void setLastName(String lastName) 
         this.lastName = lastName;
     
 
     public String getEmail() 
         return email;
     
 
     public void setEmail(String email) 
         this.email = email;
     
 
     public String getGender() 
         return gender;
     
 
     public void setGender(String gender) 
         this.gender = gender;
     
 
     public Department getDepartment() 
         return department;
     
 
     public void setDepartment(Department department) 
         this.department = department;
     
 
     @Override
     public String toString() 
         return "Employee [id=" + id + ", lastName=" + lastName + ", email=" + email + ", gender=" + gender + "]";
     
 
 

接口文件EmployeeMapper.java:

 void addEmp(Employee employee);
     Employee getEmpById(int id);

junit测试类:

 

 @Test
     public void test01() 
         String resource = "mybatis-config.xml";
         SqlSession openSession = null;
         SqlSession openSession2 = null;
         try 
             InputStream inputStream = Resources.getResourceAsStream(resource);
             SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
             // 获取openSession 不会自动提交数据
             openSession = sqlSessionFactory.openSession(true);
             openSession2 = sqlSessionFactory.openSession(true);
             EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
             EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
             Employee employee = mapper.getEmpById(1);
 
             System.out.println("测试二级缓存:" + employee);
             openSession.close();
             Employee employee1 = mapper2.getEmpById(1);
             System.out.println("测试二级缓存:" + employee1);
             openSession2.close();
          catch (Exception e) 
             // TODO: handle exception
          finally 
             if (openSession != null) 
                 openSession.close();
                 openSession2.close();
             
 
         
     

运行结果:

运行结果说明:

Cache Hit Ratio [dao.EmployeeMapper]: 0.5 且没有 发送相同的SQL,说明 :第二次的记录是从缓存Cache Hit Ratio中取出的;

 

  和缓存有关的设置和属性:

  1.cacheEnabled = true; false会关闭二级缓存;不会关闭一级缓存

  2.每个select 都有 useCache="true" 若为false 关闭的是二级缓存一级缓存没有关闭

  3. 每个增删标签的:flushCache="true" 增删改操作执行完成后就清除缓存 一级缓存失效;二级缓存也会失效

  每个select 都有 flushCache="false" 若为true 一级缓存失效;二级缓存也会失效

即:

 <select id="getEmpById" parameterType="Integer" resultType="entity.Employee" flushCache="true">
         select LAST_NAME AS
         lastName,gender as gender,email as email from
         tbl_employee where id =#asdsdfsdf
     </select>

运行结果如下图:发送了两次sql;一级缓存失效;二级缓存也会失效;

  4. openSession2.clearCache(); 是清空的是一级缓存,不会影响二级缓存

  5.localCacheScope:本地缓存作用域(一级缓存session 当前会话的所有数据保存在会话中) statement可以禁用一级缓存

 

我从来不相信什么懒洋洋的自由。我向往的自由是通过勤奋和努力实现的更广阔的人生。 我要做一个自由又自律的人,靠势必实现的决心认真地活着。

以上是关于五mybatis两级缓存的主要内容,如果未能解决你的问题,请参考以下文章

Mybatis的两级缓存初解

Mybatis的两级缓存初解

Mybatis的两级缓存初解

两级缓存,像子弹一样飞…

SSM框架MyBatis笔记 --- 表之间的关联关系;MyBatis事务;MyBatis缓存机制;ORM概述

MyBatis缓存