学堂 学堂 学堂公众号手机端

五、mybatis两级缓存

lewis 1年前 (2024-03-24) 阅读数 5 #技术
一级缓存:(本地缓存) sqlSession级别的缓存。一级缓存是一直开启的。与数据库同一次会话期间查询到的数据会放在本地缓存中。以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查数据库。验证实例(实时使用上一篇的目录结构):

接口文件EmployeeMapper.java:

Employee getEmpById(int

映射文件EmployeeMapper.xml

1 <select id="getEmpById" parameterType="Integer" resultType="entity.Employee">
2 select LAST_NAME AS
3 lastName,gender as gender,email as email from
4 tbl_employee where id =#{asdsdfsdf}
5 </select>

junit测试类:


1 @Test
2 public void test02() {
3 String resource = "mybatis-config.xml";
4 SqlSession openSession = null;
5 try {
6 InputStream inputStream = Resources.getResourceAsStream(resource);
7 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
8 // 获取openSession 不会自动提交数据
9 openSession = sqlSessionFactory.openSession(true);
10 EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
11 Employee employee = mapper.getEmpById(1);
12 Employee employee1 = mapper.getEmpById(1);
13 System.out.println("测试一级缓存:" + employee);
14 System.out.println("测试一级缓存:" + employee1 + "," + (employee == employee1));
15 } catch (Exception e) {
16 // TODO: handle exception
17 } finally {
18 if (openSession != null) {
19 openSession.close();
20 }
21
22 }
23

运行结果:

测试一级缓存: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测试类:

1 @Test
2 public void test02() {
3 String resource = "mybatis-config.xml";
4 SqlSession openSession = null;
5 SqlSession openSession2 = null;
6 try {
7 InputStream inputStream = Resources.getResourceAsStream(resource);
8 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
9 // 获取openSession 不会自动提交数据
10 openSession = sqlSessionFactory.openSession(true);
11 openSession2 = sqlSessionFactory.openSession(true);
12 EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
13 Employee employee = mapper.getEmpById(1);
14 Employee employee1 = mapper.getEmpById(1);
15 System.out.println("测试一级缓存:" + employee);
16 System.out.println("测试一级缓存:" + employee1 + "," + (employee == employee1));
17
18 EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
19 Employee employee2 = mapper2.getEmpById(1);
20 System.out.println("sqlSession改变 一级缓存失效:" + employee2 + "," + (employee1 == employee2));
21 EmployeeMapper mapper23 = openSession2.getMapper(EmployeeMapper.class);
22 Employee employee23 = mapper23.getEmpById(2);
23 System.out.println("查询条件改变改变 一级缓存失效:" + employee23 + "," + (employee1 == employee23));
24
25 Employee emp = new Employee();
26 emp.setLastName("Tom");
27 emp.setGender("男");
28 emp.setEmail("678@qq.com");
29 mapper23.addEmp(emp);
30 Employee employee4 = mapper23.getEmpById(2);
31 System.out.println("查询条件相同+sqlSession相同 一级缓存失效:" + employee23 + "," + (employee4 == employee23));
32
33 Employee employee5 = mapper23.getEmpById(2);
34 openSession2.clearCache();
35 Employee employee6 = mapper23.getEmpById(2);
36 System.out.println("手动清楚缓存:" + (employee5 == employee6));
37 } catch (Exception e) {
38 // TODO: handle exception
39 } finally {
40 if (openSession != null) {
41 openSession.close();
42 }
43
44 }
45

运行结果:如图:

二级缓存:(全局缓存) 基于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:

1 <?xml version="1.0" encoding="UTF-8" ?>
2 <!DOCTYPE configuration
3 PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
4 "http://mybatis.org/dtd/mybatis-3-config.dtd">
5 <configuration>
6 <properties resource="dbconfig.properties"></properties>
7
8 <settings>
9 <setting name="cacheEnabled" value="true" />
10 </settings>
11
12 <environments default="development">
13 <environment id="development">
14 <transactionManager type="JDBC" />
15 <dataSource type="POOLED">
16 <property name="driver" value="${jdbc.driver}" />
17 <property name="url" value="${jdbc.url}" />
18 <property name="username" value="${jdbc.username}" />
19 <property name="password" value="${jdbc.password}" />
20 </dataSource>
21 </environment>
22 <environment id="development_mysql2">
23 <transactionManager type="JDBC" />
24 <dataSource type="POOLED">
25 <property name="driver" value="${jdbc2.driver}" />
26 <property name="url" value="${jdbc2.url}" />
27 <property name="username" value="${jdbc2.username}" />
28 <property name="password" value="${jdbc2.password}" />
29 </dataSource>
30 </environment>
31 </environments>
32
33 <databaseIdProvider type="DB_VENDOR">
34 <!-- 为不同的数据库厂商取别名 -->
35 <property name="MySQL" value="mysql" />
36 <property name="Oracle" value="oracle" />
37 <property name="SQL Server" value="sqlserver" />
38 </databaseIdProvider>
39 <mappers>
40 <package name="dao" />
41 </mappers>
42 </configuration>

View Code

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

cache 标签的属性说明:

eviction:缓存回收策略;flushInterval:缓存刷新间隔,多长时间清空一次,默认不清空readOnly:缓存只读;true: mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据;mybatis为了加快速度,直接把数据在缓存中的引用交给用户;不安全;mybatis局的数据可能会被修改。mybatis会利用序列化和反序列化的技术克隆一份新数据给用户;安全,速度慢size:存放多少元素;type:指定自定义缓存的全类名;实现cache接口即可;
1 <?xml version="1.0" encoding="UTF-8" ?>
2 <!DOCTYPE mapper
3 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
5 <mapper namespace="dao.EmployeeMapper">
6 <cache></cache>18 <insert id="addEmp" parameterType="entity.Employee" useGeneratedKeys="true" keyProperty="id">
19 insert into tbl_employee(last_name,gender,email) values(
20 #{lastName},#{gender},#{email}
21 )
22 </insert>
23
24 <select id="getEmpById" parameterType="Integer" resultType="entity.Employee">
25 select LAST_NAME AS
26 lastName,gender as gender,email as email from
27 tbl_employee where id =#{asdsdfsdf}
28 </select>
29
30 </mapper>

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

1 package entity;
2
3 import java.io.Serializable;
4
5 public class Employee implements Serializable {
6
7 /**
8 *
9 */
10 private static final long serialVersionUID = -6962919367201266002L;
11 private Integer id;
12 private String lastName;
13 private String email;
14 private String gender;
15 private Department department;
16
17 public Integer getId() {
18 return id;
19 }
20
21 public void setId(Integer id) {
22 this.id = id;
23 }
24
25 public String getLastName() {
26 return lastName;
27 }
28
29 public void setLastName(String lastName) {
30 this.lastName = lastName;
31 }
32
33 public String getEmail() {
34 return email;
35 }
36
37 public void setEmail(String email) {
38 this.email = email;
39 }
40
41 public String getGender() {
42 return gender;
43 }
44
45 public void setGender(String gender) {
46 this.gender = gender;
47 }
48
49 public Department getDepartment() {
50 return department;
51 }
52
53 public void setDepartment(Department department) {
54 this.department = department;
55 }
56
57 @Override
58 public String toString() {
59 return "Employee [id=" + id + ", lastName=" + lastName + ", email=" + email + ", gender=" + gender + "]";
60 }
61
62

View Code

接口文件EmployeeMapper.java:

1 void addEmp(Employee employee);
2 Employee getEmpById(int

junit测试类:

1 @Test
2 public void test01() {
3 String resource = "mybatis-config.xml";
4 SqlSession openSession = null;
5 SqlSession openSession2 = null;
6 try {
7 InputStream inputStream = Resources.getResourceAsStream(resource);
8 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
9 // 获取openSession 不会自动提交数据
10 openSession = sqlSessionFactory.openSession(true);
11 openSession2 = sqlSessionFactory.openSession(true);
12 EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
13 EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
14 Employee employee = mapper.getEmpById(1);
15
16 System.out.println("测试二级缓存:" + employee);
17 openSession.close();
18 Employee employee1 = mapper2.getEmpById(1);
19 System.out.println("测试二级缓存:" + employee1);
20 openSession2.close();
21 } catch (Exception e) {
22 // TODO: handle exception
23 } finally {
24 if (openSession != null) {
25 openSession.close();
26 openSession2.close();
27 }
28
29 }
30

运行结果:

运行结果说明:

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 一级缓存失效;二级缓存也会失效

即:

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

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

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

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

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



版权声明

本文仅代表作者观点,不代表博信信息网立场。

热门