Mybatis一级缓存和二级缓存分别是什么,区别是什么?

本文详细解析MyBatis中一级缓存与二级缓存的工作原理及区别,包括缓存的存储方式、缓存的生效条件及缓存失效的情况等,并通过具体实例演示一级缓存的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、概念

        1.一级缓存:是基于数据库会话的,并且默认开启。一级缓存的作用域为SqlSession。在同一个SqlSession中,执行相同的sql语句,那么第一次就会去数据库中进行查询,并写到缓存中,如果我们后面还想去访问数据库查询,就直接去一级缓存中获取就可以了。

        2.二级缓存:是基于全局的,不能默认开启,开启时需要手动配置。二级缓存的作用域为SqlSessionFactory,是一个映射器级别的缓存,针对不同namespace的映射器。一个会话中,查询一条数据,这个数据会被放到一级缓存中,但是一旦这个会话关闭,一级缓存汇总的数据就会被保存到二级缓存。新的会话查询信息就会参照二级缓存中存储的信息。

二、工作流程

        1.一级缓存

         我们能从图中看出:

                a.对于某个Select Statement,根据Statement生成key;

                b.判断在local cache中,该key是否用对应的数据存在

                c.判断查询结果如果不为空则跳过数据库继续执行下面的语句

                d.如果为空,就去数据库中查询数据,能取到一个结果

                e.将key和查询到的结果作为key和value,放入local cache中

                f.判断缓存级别是否为statement级别,如果是,清空本地缓存

        2.二级缓存

         二级缓存的大致流程:

                a.开启两个会话

                b.在会话一中查询结果加入一级缓存

                c.在会话二中查询二级缓存是否有数据

                d.如果有数据取二级缓存中的数据

                e.如果没有就去查询一级缓存中是否有数据

                f.如果没有那么连接数据库进行查询

                g.如果有就取一级缓存中的数据

三、一级缓存失效情况

        1.不同sqlSesiion对应不同的一级缓存

        2.同一个sqlSession单查询条件不同

        3.同一个sqlSession两次查询期间执行了任何一次增删改操作

        4.同一个sqlSession两次查询期间手动清空了缓存

四、一级缓存与二级缓存的区别

        1.一级缓存

                a.sqlSession级别的

                b.默认是开启的

        2.二级缓存

                a.sql SessionFactory(namespace)

                b.默认是关闭的

                c.放在二级的对象要实现对象序列化接口

                d.二级缓存可以使用第三方的

五、一级缓存和二级缓存的相同点

        1.一级缓存和二级缓存都是用perpetualcache来实现的

        2.mybatis执行更新操作后,一级缓存和二级缓存都会被刷新

六、一级缓存失效实例

       学生类:

public class Student {
	
	private int sid;
	private String sname;
	private Date birthday;
	private String ssex;
	private int classid;
	public Student(int sid, String sname, Date birthday, String ssex, int classid) {
		super();
		this.sid = sid;
		this.sname = sname;
		this.birthday = birthday;
		this.ssex = ssex;
		this.classid = classid;
	}
	public Student() {
		super();
	}
	public int getSid() {
		return sid;
	}
	public void setSid(int sid) {
		this.sid = sid;
	}
	public String getSname() {
		return sname;
	}
	public void setSname(String sname) {
		this.sname = sname;
	}
	public Date getBirthday() {
		return birthday;
	}
	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}
	public String getSsex() {
		return ssex;
	}
	public void setSsex(String ssex) {
		this.ssex = ssex;
	}
	public int getClassid() {
		return classid;
	}
	public void setClassid(int classid) {
		this.classid = classid;
	}

        Dao层 :

public class DaoUtil {
	
	private static SqlSessionFactory build;
	static {
		
		try {
			InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
			build=new SqlSessionFactoryBuilder().build(resourceAsStream);
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public static SqlSession getSqlSession() {
		return build.openSession();
	}
	
	
	public static void closeResource(SqlSession SqlSession) {
		
		SqlSession.close();
	}

}

        接口层:

public interface StudentMapper {
	
	@Select("select * from student")
	public List<Student> findAllStudent();
	
	@Select("select * from student where sid=#{sid}")
	public Student findStudentBysid(int sid);
	
	
	@Insert("insert into student(sname) values('张三')")
	public int addStudent();
	
}

        测试:

public static void main(String[] args) {
		
		SqlSession sqlSession = DaoUtil.getSqlSession();
		
		StudentMapper stumapper = sqlSession.getMapper(StudentMapper.class);
		Student s = stumapper.findStudentBysid(5);
		
		System.out.println(s);
		
		 for(int i = 0; i< 100;i++) {
			 System.out.print(".");
		 }
		 // 增删改
//		 int ret = stumapper.addStudent();
		 
		 // 清空缓存
		 sqlSession.clearCache();
		 
		 System.out.println();
		

		 Student s2 = stumapper.findStudentBysid(5);
		 System.out.println(s2);
		 
		 System.out.println(s == s2);
		 
		 DaoUtil.closeResource(sqlSession);
		

	}

}

        运行之后我们能看到只要手动清空缓存,那么一级缓存就会失效。

当我们把代码改成新增时再次运行:

public class Demo2 {

	public static void main(String[] args) {
		
		SqlSession sqlSession = DaoUtil.getSqlSession();
		
		StudentMapper stumapper = sqlSession.getMapper(StudentMapper.class);
		Student s = stumapper.findStudentBysid(4);
		
		System.out.println(s);
		
		 for(int i = 0; i< 100;i++) {
			 System.out.print(".");
		 }
		 // 增删改
		 int ret = stumapper.addStudent();
		 
		 // 清空缓存
//		 sqlSession.clearCache();
		 
		 System.out.println();

		 Student s2 = stumapper.findStudentBysid(4);
		 System.out.println(s2);
		 
		 System.out.println(s == s2);
		 
		 DaoUtil.closeResource(sqlSession);
		

	}

}

答案还是false,说明新增情况下,一级缓存也会失效,此外删除和修改也是同样的效果。

当然不同的sqlSession对象也会使一级缓存失效:

public class Demo3 {

	public static void main(String[] args) {
		
		SqlSession sqlSession1 = DaoUtil.getSqlSession();
		
		StudentMapper mapper1 = sqlSession1.getMapper(StudentMapper.class);
		Student s1 = mapper1.findStudentBysid(5);
		System.out.println(s1);
		
		DaoUtil.closeResource(sqlSession1);
		
		
		SqlSession sqlSession2 = DaoUtil.getSqlSession();
		
		StudentMapper mapper2 = sqlSession2.getMapper(StudentMapper.class);
		Student s2 = mapper2.findStudentBysid(5);
		System.out.println(s2);
		
		DaoUtil.closeResource(sqlSession2);
		
		System.out.println(s1 == s2);
		

	}

}

运行情况如下图:

 

 七、总结

        mybatis的的一级缓存是SqlSession级别的缓存,一级缓存缓存的是对象,当SqlSession提

交、关闭以及其他的更新数据库的操作发生后,一级缓存就会清空。二级缓存SqlSessionFactory

级别的缓存,同一个SqlSessionFactory产生的SqlSession都共享一个二级缓存,二级缓存中存储

的是数据,当命中二级缓存时,通过存储的数据构造对象返回。查询数据的时候,查询的流程是二

级缓存>一级缓存>数据库。

<think>好的,我现在要仔细分析用户的问题,他想了解MyBatis一级缓存二级缓存区别及工作原理。首先,我需要回忆MyBatis缓存的相关知识,并结合提供的引用资料来组织答案。 根据用户提供的引用内容,引用[2][3]提到一级缓存是SqlSession级别的,默认开启,而二级缓存是Mapper级别的,默认关闭。引用[4]则说明了如何配置启用二级缓存,需要修改配置文件并在Mapper文件中添加缓存设置。此外,引用[2][3]都指出,二级缓存在增删改频繁的情况下可能无效,因为会被清空。 接下来需要明确两者的区别,可能包括作用域、开启状态、存储位置、失效机制等。例如,一级缓存存在于SqlSession的生命周期中,而二级缓存跨SqlSession,属于Mapper级别。一级缓存默认开启,二级缓存需要手动配置。存储位置上,一级缓存在内存,二级缓存可能使用外部存储如Redis。失效机制方面,一级缓存在执行update操作或关闭SqlSession时失效,二级缓存在整个Mapper的update操作后失效。 另外,需要解释工作原理。一级缓存如何工作:执行查询时先检查缓存,存在则直接返回,否则查询数据库并存入缓存二级缓存则需要跨会话共享,可能涉及序列化问题,且需注意事务提交后才生效。 用户可能存在的疑问点包括:为什么二级缓存默认关闭?如何正确使用二级缓存?适用场景是什么?这些都需要在回答中涵盖。 需要确保回答结构清晰,分点说明区别,再详细各自的工作原理,最后总结注意事项。同时,引用资料中的信息要正确标注,如默认关闭的原因,配置方法等。 可能需要注意的问题:是否存在引用资料中的重复?比如引用[2][3]内容相似,需要确认是否不同来源,但用户提供的引用可能有重复,处理时应合并相同点。此外,确保术语准确,比如SqlSession、Mapper等。 最后,生成相关问题,可能涉及性能优化、配置步骤、使用场景等,根据用户提供的示例问题来参考结构。</think>### MyBatis 一级缓存二级缓存区别及工作原理 #### 一、核心区别 1. **作用域不同** - **一级缓存**:作用于单个 `SqlSession` 生命周期内,缓存数据仅对当前会话可见[^2][^3]。 - **二级缓存**:作用于 `Mapper` 级别,多个 `SqlSession` 共享同一命名空间(Mapper接口)的缓存数据[^2][^3]。 2. **默认状态与配置** - **一级缓存**:默认开启,无需额外配置[^3]。 - **二级缓存**:默认关闭,需在全局配置中启用(`cacheEnabled=true`),并在对应的 Mapper XML 文件中添加 `<cache/>` 标签[^4]。 3. **失效机制** - **一级缓存**: - 执行增删改(`INSERT/UPDATE/DELETE`)操作时自动清空当前 `SqlSession` 的缓存。 - 调用 `SqlSession.close()` 或 `SqlSession.clearCache()` 时清空。 - **二级缓存**: - 执行同一 Mapper 的增删改操作时清空整个命名空间的缓存。 - 需注意事务提交后(`sqlSession.commit()`)才会生效。 4. **存储位置** - **一级缓存**:基于内存(JVM 堆)存储,直接关联 `SqlSession` 对象。 - **二级缓存**:可通过配置使用第三方缓存(如 Redis、EhCache),支持跨会话持久化。 #### 二、工作原理 1. **一级缓存的工作流程** - 查询时优先检查缓存,若命中则直接返回结果。 - 未命中则查询数据库,将结果存入缓存(键为 SQL 语句 + 参数 + 分页条件)。 - 执行更新操作或会话关闭时,清空缓存以保证数据一致性[^3]。 ```java // 示例:同一 SqlSession 内重复查询会命中缓存 User user1 = sqlSession.selectOne("getUserById", 1); // 查数据库 User user2 = sqlSession.selectOne("getUserById", 1); // 直接读缓存 ``` 2. **二级缓存的工作流程** - 查询时先检查一级缓存,未命中则检查二级缓存。 - 二级缓存命中后,数据会反序列化并存入当前会话的一级缓存。 - 事务提交时,当前会话的一级缓存数据才会同步到二级缓存[^4]。 - 需注意:返回对象需实现 `Serializable` 接口,否则可能抛出序列化异常。 ```xml <!-- Mapper XML 中配置二级缓存 --> <mapper namespace="com.example.UserMapper"> <cache/> </mapper> ``` #### 三、注意事项 - **二级缓存的适用场景**: - 数据读多写少,且对实时性要求不高(如配置表查询)[^3]。 - 若频繁执行增删改操作,二级缓存可能频繁失效,反而降低性能。 - **缓存一致性风险**: - 分布式环境下,二级缓存需配合集中式缓存(如 Redis)避免数据不一致。 - **强制跳过缓存**: - 可通过 `flushCache="true"` 属性强制清空缓存,例如: ```xml <select id="getUser" resultType="User" flushCache="true"> ... </select> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值