一、mybatis的一级缓存(含项目示例演示)
1、示例目录结构
mybatis_cache/
├── C:/Users/杨智慧/Desktop/SSM/mybatis_cache/
│ ├── .idea/
│ ├── src/
│ │ ├── main/
│ │ │ ├── java/
│ │ │ │ ├── com.atguigu.mybatis/
│ │ │ │ │ ├── mapper/
│ │ │ │ │ │ └── CacheMapper.java
│ │ │ │ │ ├── pojo/
│ │ │ │ │ │ └── Emp.java
│ │ │ │ │ └── utils/
│ │ │ │ │ └── SqlSessionUtil.java
│ │ │ ├── resources/
│ │ │ │ ├── com.atguigu.mybatis.mapper/
│ │ │ │ │ └── CacheMapper.xml
│ │ │ │ ├── jdbc.properties
│ │ │ │ ├── log4j.xml
│ │ │ │ └── mybatis-config.xml
│ │ ├── test/
│ │ │ └── java/
│ │ │ └── CacheMapperTest.java
│ ├── target/
│ └── pom.xml
├── 外部库/
└── 临时文件和控制台/
2、实例演示
CacheMapper.java代码
package com.atguigu.mybatis.mapper;
import com.atguigu.mybatis.pojo.Emp;
import org.apache.ibatis.annotations.Param;
public interface CacheMapper {
/* *
*
*根据员工id查询员工信息
* @param empId
* @return com.atguigu.mybatis.pojo.Emp
* @author yzh
* @create 2025/2/24
**/
Emp getEmpById(@Param("empId") Integer empId);
}
CacheMapper.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="com.atguigu.mybatis.mapper.CacheMapper">
<!--Emp getEmpById(@Param("empId") Integer empId);-->
<select id="getEmpById" resultType="Emp">
select *
from t_emp where emp_id = #{empId};
</select>
</mapper>
CacheMapperTest.java代码
@Test
public void testGetEmpById() {
SqlSession sqlSession1 = SqlSessionUtil.getSqlSession();
CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
Emp emp1 = mapper1.getEmpById(empId: 1);
System.out.println(emp1);
Emp emp2 = mapper1.getEmpById(empId: 1);
System.out.println(emp2);
SqlSession sqlSession2 = SqlSessionUtil.getSqlSession();
CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
Emp emp3 = mapper2.getEmpById(empId: 1);
System.out.println(emp3);
}
输出结果
DEBUG 02-24 08:35:56,895 ==> Preparing: select * from t_emp where emp_id = ?; (BaseJdbcLogger.java:137)
DEBUG 02-24 08:35:56,941 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 02-24 08:35:56,971 <== Total: 1 (BaseJdbcLogger.java:137)
Emp{empId=1, empName='张三', age=20, gender='男'}
Emp{empId=1, empName='张三', age=20, gender='男'}
DEBUG 02-24 08:35:57,015 ==> Preparing: select * from t_emp where emp_id = ?; (BaseJdbcLogger.java:137)
DEBUG 02-24 08:35:57,016 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 02-24 08:35:57,018 <== Total: 1 (BaseJdbcLogger.java:137)
Emp{empId=1, empName='张三', age=20, gender='男'}
进程已结束,退出代码为 0
3、分析总结
MyBatis 的一级缓存:
-
MyBatis 的一级缓存是
SqlSession
级别的缓存,默认开启。 -
在同一个
SqlSession
中,执行相同的 SQL 查询时,第一次查询的结果会被缓存,后续相同的查询会直接从缓存中获取,而不会再次访问数据库。 -
一级缓存在
SqlSession
关闭或执行了增删改操作后会被清空。
输出结果分析:
-
在
CacheMapperTest
中,sqlSession1
第一次查询empId=1
时,会执行 SQL 查询并将结果缓存。 -
第二次查询
empId=1
时,直接从缓存中获取结果,不会再次执行 SQL。 -
使用
sqlSession2
查询empId=1
时,由于是不同的SqlSession
,会再次执行 SQL 查询。
二、使一级缓存失效的四种情况
MyBatis 的一级缓存是 SqlSession
级别的缓存,默认情况下,同一个 SqlSession
中执行相同的查询会从缓存中获取数据。然而,在某些情况下,一级缓存会失效,导致再次查询时会重新访问数据库。以下是使一级缓存失效的四种情况:
1. 执行了增删改操作(INSERT、UPDATE、DELETE)
当执行了 INSERT
、UPDATE
或 DELETE
操作时,MyBatis 会清空当前 SqlSession
的一级缓存,以确保缓存中的数据与数据库保持一致。
以INSERT作为示例:
CacheMapper.java代码
/* *
*
* 添加员工信息
* @param emp
* @return void
* @author yzh
* @create 2025/2/24
**/
void insertEmp(Emp emp);
CacheMapper.xml代码
<!--Emp getEmpById(@Param("empId") Integer empId);-->
<select id="getEmpById" resultType="Emp">
select *
from t_emp where emp_id = #{empId};
</select>
<!--void insertEmp(Emp emp);-->
<insert id="insertEmp">
insert into t_emp values (null,#{empName},#{age},#{gender},null)
</insert>
CacheMapperTest.java代码
@Test
public void testGetEmpById() {
SqlSession sqlSession1 = SqlSessionUtil.getSqlSession();
CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
Emp emp1 = mapper1.getEmpById(1);
System.out.println(emp1);
mapper1.insertEmp(new Emp( null, "小红", 25, "男"));
Emp emp2 = mapper1.getEmpById(1);
System.out.println(emp2);
}
输出结果
DEBUG 02-24 08:51:36,369 ==> Preparing: select * from t_emp where emp_id = ?; (BaseJdbcLogger.java:137)
DEBUG 02-24 08:51:36,395 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 02-24 08:51:36,415 <== Total: 1 (BaseJdbcLogger.java:137)
Emp{empId=1, empName='张三', age=20, gender='男'}
DEBUG 02-24 08:51:36,417 ==> Preparing: insert into t_emp values (null,?,?,?,null) (BaseJdbcLogger.java:137)
DEBUG 02-24 08:51:36,418 ==> Parameters: 小红(String), 25(Integer), 男(String) (BaseJdbcLogger.java:137)
DEBUG 02-24 08:51:36,419 <== Updates: 1 (BaseJdbcLogger.java:137)
DEBUG 02-24 08:51:36,419 ==> Preparing: select * from t_emp where emp_id = ?; (BaseJdbcLogger.java:137)
DEBUG 02-24 08:51:36,419 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 02-24 08:51:36,420 <== Total: 1 (BaseJdbcLogger.java:137)
Emp{empId=1, empName='张三', age=20, gender='男'}
进程已结束,退出代码为 0
分析:
-
第一次查询
empId=1
时,会执行 SQL 并将结果缓存。 -
执行 INSERT操作后,缓存被清空。
-
第二次查询
empId=1
时,由于缓存已失效,会再次执行 SQL。
2. 手动清空缓存
除了执行增删改操作外,还可以通过调用 SqlSession
的 clearCache()
方法手动清空一级缓存。这种方式适用于需要强制刷新缓存的场景。
示例代码:
CacheMapperTest.java 代码
@Test
public void testCacheInvalidationByClearCache() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
CacheMapper mapper = sqlSession.getMapper(CacheMapper.class);
Emp emp1 = mapper.getEmpById(1);
System.out.println(emp1);
sqlSession.clearCache();
Emp emp2 = mapper.getEmpById(1);
System.out.println(emp2);
}
输出结果
DEBUG ==> Preparing: SELECT * FROM t_emp WHERE emp_id = ?;
DEBUG ==> Parameters: 1(Integer)
DEBUG <== Total: 1
Emp{empId=1, empName='张三', age=20, gender='男'}
DEBUG ==> Preparing: SELECT * FROM t_emp WHERE emp_id = ?;
DEBUG ==> Parameters: 1(Integer)
DEBUG <== Total: 1
Emp{empId=1, empName='张三', age=20, gender='男'}
分析:
-
第一次查询
empId=1
时,会执行 SQL 并将结果缓存。 -
调用
clearCache()
方法后,缓存被清空。 -
第二次查询
empId=1
时,由于缓存已失效,会再次执行 SQL。
3. 提交或回滚事务
当调用 SqlSession
的 commit()
或 rollback()
方法时,一级缓存会被清空。这是因为事务的提交或回滚可能改变了数据库的状态,缓存需要与数据库保持一致。
示例代码:
CacheMapperTest.java 代码
@Test
public void testCacheInvalidationByCommit() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
CacheMapper mapper = sqlSession.getMapper(CacheMapper.class);
Emp emp1 = mapper.getEmpById(1);
System.out.println(emp1);
sqlSession.commit();
Emp emp2 = mapper.getEmpById(1);
System.out.println(emp2);
}
输出结果
DEBUG ==> Preparing: SELECT * FROM t_emp WHERE emp_id = ?;
DEBUG ==> Parameters: 1(Integer)
DEBUG <== Total: 1
Emp{empId=1, empName='张三', age=20, gender='男'}
DEBUG ==> Preparing: SELECT * FROM t_emp WHERE emp_id = ?;
DEBUG ==> Parameters: 1(Integer)
DEBUG <== Total: 1
Emp{empId=1, empName='张三', age=20, gender='男'}
分析:
-
第一次查询
empId=1
时,会执行 SQL 并将结果缓存。 -
调用
commit()
方法后,缓存被清空。 -
第二次查询
empId=1
时,由于缓存已失效,会再次执行 SQL。
4. 关闭 SqlSession
当 SqlSession
被关闭时,一级缓存也会被清空。这是因为一级缓存的生命周期与 SqlSession
绑定,SqlSession
关闭后,缓存自然失效。
示例代码:
CacheMapperTest.java 代码
@Test
public void testCacheInvalidationByClose() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
CacheMapper mapper = sqlSession.getMapper(CacheMapper.class);
Emp emp1 = mapper.getEmpById(1);
System.out.println(emp1);
sqlSession.close();
SqlSession newSqlSession = SqlSessionUtil.getSqlSession();
CacheMapper newMapper = newSqlSession.getMapper(CacheMapper.class);
Emp emp2 = newMapper.getEmpById(1);
System.out.println(emp2);
}
输出结果
DEBUG ==> Preparing: SELECT * FROM t_emp WHERE emp_id = ?;
DEBUG ==> Parameters: 1(Integer)
DEBUG <== Total: 1
Emp{empId=1, empName='张三', age=20, gender='男'}
DEBUG ==> Preparing: SELECT * FROM t_emp WHERE emp_id = ?;
DEBUG ==> Parameters: 1(Integer)
DEBUG <== Total: 1
Emp{empId=1, empName='张三', age=20, gender='男'}
分析:
-
第一次查询
empId=1
时,会执行 SQL 并将结果缓存。 -
关闭
SqlSession
后,缓存被清空。 -
重新获取
SqlSession
后,第二次查询empId=1
时,由于缓存已失效,会再次执行 SQL。
5.总结
MyBatis 的一级缓存在以下四种情况下会失效:
-
执行了增删改操作:
INSERT
、UPDATE
、DELETE
操作会清空缓存。 -
手动清空缓存:调用
SqlSession.clearCache()
方法。 -
提交或回滚事务:调用
SqlSession.commit()
或rollback()
方法。 -
关闭 SqlSession:
SqlSession
关闭后,缓存会被清空。