MyBatis缓存

本文详细介绍了MyBatis的一级缓存和二级缓存机制。一级缓存是SqlSession级别的,提高查询效率,而二级缓存是namespace级别的,可跨SqlSession。在配置和实践中,需要注意缓存更新和清除,以及可能的脏读问题。二级缓存要求返回的POJO实现Serializable接口。总结了缓存的工作流程和注意事项。

1.  为什么使用缓存?

当用户频繁查询某些固定的数据时,第一次将这些数据从数据库中查询出来,保存在缓存中。当用户再  次查询这些数据时,不用再通过数据库查询,而是去缓存里面查询。减少网络连接和数据库查询带来的损 ,从而提高我们的查询效率,减少高并发访问带来的系统性能问题。

一句话概括:经常查询一些不经常发生变化的数据,使用缓存来提高查询效率。

像大多数的持久化框架一样,Mybatis也提供了缓存策略,通过缓存策略来减少数据库的查询次数,  从而提高性能。 Mybatis中缓存分为一级缓存,二级缓存。

2. 一级缓存

2.1 介绍

一级缓存是SqlSession级别的缓存,是默认开启的

所以在参数和SQL完全一样的情况下,我们使用同一个SqlSession对象调用一个Mapper方法,往往  只执行一次SQL,因为使用SelSession第一次查询后,MyBatis会将其放在缓存中,以后再查询的时

候,如果没有声明需要刷新,并且缓存没有超时的情况下,SqlSession都会取出当前缓存的数据,而不  会再次发送SQL到数据库。

2.2 验证

@Test
public void testOneCache() throws Exception { SqlSession sqlSession = MyBatisUtils.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

User user1 = userMapper.findById(1); System.out.println("第一次查询的用户:" + user1);

User user2 = userMapper.findById(1); System.out.println("第二次查询的用户:" + user2);

sqlSession.close();
}

我们可以发现,虽然在上面的代码中我们查询了两次,但最后只执行了一次数据库操作,这就是Mybatis提供给我们的一级缓存在起作用了。因为一级缓存的存在,导致第二次查询id1的记录时,并  没有发出sql语句从数据库中查询数据,而是从一级缓存中查询。

2.3 分析

一级缓存是SqlSession范围的缓存,执行SqlSessionC(增加)U(更新)D(删除)操作,或者调  clearCache()commit()close()方法,都会清空缓存。

  • 第一次发起查询用户id41的用户信息,先去找缓存中是否有id41的用户信息,如果没有,从数据库  查询用户信息。
  • 得到用户信息,将用户信息存储到一级缓存中。
  • 如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这  样做的目的为了让缓存中存储的是最新的信息,避免脏读。
  • 第二次发起查询用户id41的用户信息,先去找缓存中是否有id41的用户信息,缓存中有,直接从缓  存中获取用户信息。

2.4 清除

@Test
public void testClearOneCache() throws Exception { SqlSession sqlSession = MybatisUtils.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user1 = userMapper.findById(41);
System.out.println("第一次查询的用户:" + user1);

//调用sqlSession清除缓存的方法sqlSession.clearCache();

User user2 = userMapper.findById(41); System.out.println("第二次查询的用户:" + user2);
}
<!-- 每次查询时,都会清除缓存 -->
< select flushCache="true"></select>

3. 二级缓存

3.1 介绍

二级缓存是namspace级别(跨sqlSession)的缓存,是默认不开启的

二级缓存的开启需要进行配置,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的。     也就是要求实现Serializable接口,配置方法很简单,只需要在映射XML文件配置<cache/> 就可以开启二级缓存了。

3.2 验证

1)配置核心配置文件

<settings>
<!--
因为cacheEnabled的取值默认就为true,所以这一步可以省略不配置。 为true代表开启二级缓存;为false代表不开启二级缓存。
-->
<setting name="cacheEnabled" value="true"/>
</settings>

2)配置UserMapper.xml映射

<mapper namespace="com.lagou.dao.UserMapper">
<!--当前映射文件开启二级缓存-->
<cache></cache>
<!--
<select>标签中设置useCache=”true”代表当前这个statement要使用二级缓存。 如果不使用二级缓存可以设置为false
注意:
针对每次查询都需要最新的数据sql,要设置成useCache="false",禁用二级缓存。
-->
<select id="findById" parameterType="int" resultType="user" useCache="true"
>
SELECT * FROM `user` where id = #{id}
</select>
</mapper>

3)修改User实体

public class User implements Serializable { private Integer id;
private String username; private Date birthday; private String sex; private String address;

private List<Role> roleList; private List<Order> orderList;
}

4)测试结果

@Test
public void testTwoCache() throws Exception { SqlSession sqlSession = MyBatisUtils.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user = userMapper.findById(41);
System.out.println("第一次查询的用户:" + user);
sqlSession.close();

SqlSession sqlSession1 = MyBatisUtils.openSession();
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class); User user1 = userMapper1.findById(41);
System.out.println("第二次查询的用户:"+user1);
sqlSession1.close();
}

3.3 分析

二级缓存是mapper映射级别的缓存,多个SqlSession去操作同一个Mapper映射的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

 

  • 映射语句文件中的所有select语句将会被缓存。
  • 映射语句文件中的所有insertupdatedelete语句会刷新缓存。

3.4 注意问题(脏读)

mybatis的二级缓存因为是namespace级别,所以在进行多表查询时会产生脏读问题

4. 小结

  • mybatis的缓存,都不需要我们手动存储和获取数据。mybatis自动维护的。
  • mybatis开启了二级缓存后,那么查询顺序:二级缓存--》一级缓存--》数据库
  • 注意:mybatis的二级缓存会存在脏读问题,需要使用第三方的缓存技术解决问题。

节选自拉钩教育JAVA系列课程

 

 

 

 

 

 

 

### 三级标题:MyBatis 缓存机制的工作原理 MyBatis 提供了两种级别的缓存机制:一级缓存和二级缓存,分别适用于不同的使用场景,并通过特定的数据结构实现查询结果的存储与复用。 一级缓存是 SqlSession 级别的缓存,默认情况下是开启的。每个 SqlSession 都会维护一个本地缓存(基于 HashMap 实现),用于存储该 SqlSession 中执行过的查询结果。在同一个 SqlSession 中,如果执行相同的查询语句,则 MyBatis 会直接从缓存中获取数据,而不会再次访问数据库[^3]。当执行插入、更新或删除操作时,MyBatis 会自动清空当前 SqlSession 的一级缓存,以确保数据的一致性[^1]。 二级缓存是 Mapper(namespace)级别的缓存,多个 SqlSession 可以共享同一个 Mapper 的二级缓存。这意味着跨 SqlSession 的查询可以复用缓存数据,前提是这些查询属于相同的 Mapper 命名空间。二级缓存需要显式配置,可以通过接口注解方式实现,例如使用 `@CacheNamespace` 注解来启用指定 Mapper 接口的二级缓存功能[^2]。 以下是一个使用注解方式配置 MyBatis 二级缓存的示例: ```java @CacheNamespace public interface UserMapper { User getUserById(int id); } ``` ### 三级标题:MyBatis 缓存的使用注意事项 尽管缓存机制可以显著提升数据库查询性能,但在实际应用中需要注意以下几点。 首先,一级缓存的作用范围仅限于当前 SqlSession,不同 SqlSession 之间的缓存数据互不影响。因此,在涉及多个 SqlSession 或事务隔离级别较高的场景下,不能依赖一级缓存保证数据一致性[^4]。 其次,二级缓存虽然支持跨 SqlSession 共享数据,但其默认实现并不具备线程安全特性。在并发访问频繁的环境中,建议引入第三方缓存组件(如 Ehcache 或 Redis)以提高稳定性和可扩展性。此外,二级缓存的生命周期独立于 SqlSession,即使关闭或清除 SqlSession,缓存数据依然存在,因此必须谨慎管理缓存更新策略,避免出现脏读问题[^1]。 最后,由于缓存机制的存在,某些情况下可能会导致查询结果与数据库中的最新数据不一致。为了解决这一问题,可以在执行写操作后手动刷新缓存,或者通过配置合理的缓存失效时间来控制数据同步的粒度。同时,应避免对频繁更新的数据使用缓存,以免因频繁清空缓存而抵消性能优化效果[^3]。 ### 三级标题:相关代码示例 以下是一个展示 MyBatis 一级缓存行为的简单示例。在这个例子中,两次调用相同的查询方法将只触发一次数据库访问: ```java public class UserService { @Autowired private SqlSession sqlSession; public void testCache() { UserMapper mapper = sqlSession.getMapper(UserMapper.class); // 第一次查询,结果从数据库中获取 User user1 = mapper.getUserById(1); // 第二次查询相同的记录,结果从一级缓存中获取,不再访问数据库 User user2 = mapper.getUserById(1); } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值