- Mybatis对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存;
- 一级缓存只是相对于同一个SqlSession而言。在参数和SQL完全一样的情况下,我们使用同一个SqlSession对象调用一个Mapper方法,往往只执行一次SQL;
- 因为使用SelSession第一次查询后,MyBatis会将其放在缓存中;
- 以后再查询的时候,如果没有声明需要刷新,SqlSession都会取出当前缓存的数据,而不会再次发送SQL到数据库。
sql 脚本:
CREATE TABLE `t_user` (
`f_id` bigint(20) NOT NULL COMMENT 'ID主键',
`f_user_name` varchar(128) NOT NULL DEFAULT '' COMMENT '用户名字',
`f_create_time` date DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`f_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表';
INSERT INTO `test`.`t_user` (`f_id`, `f_user_name`, `f_create_time`) VALUES ('1', '张三', '2021-09-11');
User.java
@Data
public class User {
private Long id ;
private String userName ;
private Date createTime;
}
UserMapper.java
public interface UserMapper {
User selectById(Long id);
}
UserMapper.xml
上述代码完成了 对 t_user 单个查询 , 下面让我们看一下测试类:
package com.lot;
import com.alibaba.fastjson.JSONObject;
import com.lot.entity.User;
import lombok.SneakyThrows;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.InputStream;
public class MybatisStart {
@SneakyThrows
@Test
public void test1() {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获取SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 第一次查询
User user1 = sqlSession.selectOne("com.lot.mapper.UserMapper.selectById", 1);
// 打印结果: user1: name:张三
System.out.println("user1: name:" + user1.getUserName());
// 在内存中修改 UserName: 张三 -->> 张三1
user1.setUserName(user1.getUserName() + "1");
User user2 = sqlSession.selectOne("com.lot.mapper.UserMapper.selectById", 1);
// 打印结果 user2: name:张三1
System.out.println("user2: name:" + user2.getUserName());
sqlSession.close();
// 新开启一个 SqlSession
SqlSession sqlSession2 = sqlSessionFactory.openSession();
User user3 = sqlSession2.selectOne("com.lot.mapper.UserMapper.selectById", 1);
// 打印结果 user3: name:张三
System.out.println("user3: name:" + user3.getUserName());
sqlSession2.close();
}
}
通过打印结果我们可以得出:
1、同一个 SqlSession,当Sql 和 参数完全一致时,第二次查询会直接从内存中获取,非数据库
2、不同 SqlSession 之间缓存不共享。
带着上述结论,深入MyBatis源码看一下, 究竟是如何实现的。
源码剖析:如图所示:
- 我们在第一个SqlSession 第二次查询时加断点:
先看一下 SqlSession 的对象内的数据
上图所示:
当 SqlSession 执行一次Sql查询后会把 执行的 sql 等参数作为 CacheKey, 查询结果作为 value 存放在 HashMap 中,共下次查询使用
Mybatis一级缓存代码流程图如下所示:
ProcessOn 时序图
同一个 SqlSession 中下列操作会导致 一级缓存失效
- List item
Mybatis 源码相对比较简单, 一级缓存也是简简单单的存储在 HashMap中,随着 SqlSession 被JVM 回收而回收。



