Mybatis 一、二级缓存实用详解
Mybatis缓存
一级缓存:
一级缓存的作用域是sqlSession级别,同一个sqlSession中执行相同的sql查询(相同的SQL和参数),第一次会去查询数据库并写到一级缓存中,第二次则会从一级缓存中直接获取。
二级缓存:
二级缓存的作用域是sqlSessionFactory的Mapper映射级别,是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。
配置实例
Demo架构
public class Chapter3Main {
Logger log = Logger.getLogger(Chapter3Main.class);
public static void main(String[] args) {
testRoleMapper1();
//testRoleMapper2();
//testRoleMapper3();
//testRoleMapper4();
//testRoleMapper5();
}
//一级缓存 同一sqlSession执行相同的sql
private static void testRoleMapper1() {
SqlSession sqlSession = null;
try {
sqlSession = SqlSessionFactoryUtils.openSqlSession();
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
Role role = roleMapper.getRole(9);
System.out.println("role:" + role);
role = roleMapper.getRole(9);
System.out.println("role:" + role);
} finally {
if (sqlSession != null) {
sqlSession.close();
}
}
}
//一级缓存 不同的sqlSession执行相同的sql
private static void testRoleMapper2() {
SqlSession sqlSession1 = null;
SqlSession sqlSession2 = null;
try {
sqlSession1 = SqlSessionFactoryUtils.openSqlSession();
sqlSession2 = SqlSessionFactoryUtils.openSqlSession();
RoleMapper roleMapper1 = sqlSession1.getMapper(RoleMapper.class);
Role role1 = roleMapper1.getRole(9);
System.out.println("role:" + role1);
RoleMapper roleMapper2 = sqlSession2.getMapper(RoleMapper.class);
Role role2 = roleMapper2.getRole(9);
System.out.println("role:" + role2);
} finally {
if (sqlSession1 != null) {
sqlSession1.close();
}
if (sqlSession2 != null) {
sqlSession2.close();
}
}
}
//二级缓存 RoleMapper.xml 有/无<cache/>测试
private static void testRoleMapper3() {
SqlSession sqlSession1 = null;
SqlSession sqlSession2 = null;
try {
sqlSession1 = SqlSessionFactoryUtils.openSqlSession();
sqlSession2 = SqlSessionFactoryUtils.openSqlSession();
RoleMapper roleMapper1 = sqlSession1.getMapper(RoleMapper.class);
Role role1 = roleMapper1.getRole(9);
System.out.println("role:" + role1);
sqlSession1.commit();
RoleMapper roleMapper2 = sqlSession2.getMapper(RoleMapper.class);
Role role2 = roleMapper2.getRole(9);
System.out.println("role:" + role2);
sqlSession1.commit();
} finally {
if (sqlSession1 != null) {
sqlSession1.close();
}
if (sqlSession2 != null) {
sqlSession2.close();
}
}
}
//二级缓存 <setting name="cacheEnabled" value="false"/>情况下 <cache/>有无测试
private static void testRoleMapper4() {
SqlSession sqlSession1 = null;
SqlSession sqlSession2 = null;
try {
sqlSession1 = SqlSessionFactoryUtils.openSqlSession();
sqlSession2 = SqlSessionFactoryUtils.openSqlSession();
RoleMapper roleMapper1 = sqlSession1.getMapper(RoleMapper.class);
Role role1 = roleMapper1.getRole(9);
System.out.println("role:" + role1);
sqlSession1.commit();
RoleMapper roleMapper2 = sqlSession2.getMapper(RoleMapper.class);
Role role2 = roleMapper2.getRole(9);
System.out.println("role:" + role2);
sqlSession1.commit();
} finally {
if (sqlSession1 != null) {
sqlSession1.close();
}
if (sqlSession2 != null) {
sqlSession2.close();
}
}
}
//全局开启二级缓存 单独某个select关闭二级缓存
private static void testRoleMapper5() {
SqlSession sqlSession1 = null;
SqlSession sqlSession2 = null;
try {
sqlSession1 = SqlSessionFactoryUtils.openSqlSession();
sqlSession2 = SqlSessionFactoryUtils.openSqlSession();
RoleMapper roleMapper1 = sqlSession1.getMapper(RoleMapper.class);
List<Role> role1 = roleMapper1.getRoleByName("geng");
System.out.println("role:" + role1);
sqlSession1.commit();
RoleMapper roleMapper2 = sqlSession2.getMapper(RoleMapper.class);
List<Role> role2 = roleMapper2.getRoleByName("geng");
System.out.println("role:" + role2);
sqlSession1.commit();
} finally {
if (sqlSession1 != null) {
sqlSession1.close();
}
if (sqlSession2 != null) {
sqlSession2.close();
}
}
}
}
public interface RoleMapper {
public Role getRole(int id);
public ArrayList<Role> getRoleByName(@Param("roleName") String roleName);
}
@Data
public class Role implements Serializable {
private int roleId;
private String roleName;
private int roleAge;
}
public class SqlSessionFactoryUtils {
private final static Class<SqlSessionFactoryUtils> LOCK = SqlSessionFactoryUtils.class;
private static SqlSessionFactory sqlSessionFactory = null;
private SqlSessionFactoryUtils() {
}
public static SqlSessionFactory getSqlSessionFactory() {
synchronized (LOCK) {
if (sqlSessionFactory != null) {
return sqlSessionFactory;
}
String resource = "mapper/mybatis-config.xml";
InputStream inputStream;
try {
inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
return null;
}
return sqlSessionFactory;
}
}
public static SqlSession openSqlSession() {
if (sqlSessionFactory == null) {
getSqlSessionFactory();
}
return sqlSessionFactory.openSession();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases><!-- 别名 -->
<package name="com.example.mybatismapper.pojo"/>
</typeAliases>
<!-- 数据库环境 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://127.0.0.1/springmvc_db?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 映射文件 -->
<mappers>
<mapper resource="mapper/RoleMapper.xml"/>
</mappers>
</configuration>
<?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.example.mybatismapper.mapper.RoleMapper">
<select id="getRole" parameterType="int" resultType="Role" >
select *
from role
where role_id = #{id}
</select>
<select id="getRoleByName" parameterType="string" resultType="Role">
select *
from role
where role_name like concat('%', #{roleName}, '%')
</select>
</mapper>
CREATE TABLE `role` (
`role_id` int NOT NULL AUTO_INCREMENT,
`role_name` varchar(255) DEFAULT NULL,
`role_age` int DEFAULT NULL,
PRIMARY KEY (`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8
Mybatis配置setting配置项:
——该配置影响所有映射器中配置缓存的全局开关 默认true;
cache元素的属性配置自行百度;
select元素的属性配置:
flushCache——它的作用是在调用sql后,是否要求Mybatis清空之前查询本地缓存和二级缓存 默认false;
useCache——启动二级缓存的开关,是否要求Mybatis将此次结果缓存 默认值true;
一级缓存
同一个sqlSession执行了两次相同的sql,但只有第一次去数据库查询了,第二次直接从一级缓存中获取的查询结果;
两个不同的sqlSession执行相同的sql,两次sql都去数据库查询了。说明一级缓存是sqlSession级别的缓存
二级缓存
因为cacheEnabled默认是true,要想开启二级缓存则直接在映射文件中加上 cache 标签即可(各种属性可自行配置)。
两个不同的sqlSession执行相同的sql,但只有第一次去数据库查询了,第二次直接从二级缓存中获取的查询结果;
将cacheEnabled设置为false:
两个不同的sqlSession执行相同的sql,两次sql都去数据库查询了,说明没有走二级缓存。
select相关配置
useCache
cacheEnabled设置回为true:将getRoleByName sql查询语句useCache设置为"false"——全局开启二级缓存,但方法getRoleByName 关闭二级缓存(useCache细化二级缓存的应用语句)
可看出getRoleByNameSQL语句执行了两次数据库查询, 没有走二级缓存。
flushCache
一级缓存
同一个sqlSession执行了两次相同的sql,两次sql都去数据库查询了,说明没有走一级缓存。
二级缓存
两个不同的sqlSession执行相同的sql,两次sql都去数据库查询了,说明没有走二级缓存。
总结
1. 当 Mybatis 调用 Dao 层查询数据库时,会先查询二级缓存,二级缓存中无对应数据,再去查询一级缓存,一级缓存中也没有,最后去数据库查找。
2. sqlSession执行完commit操作(执行insert、update、delete)后,会清空SqlSession中的一级缓存;在关闭(close)sqlSession后,会把该sqlSession一级缓存中的数据添加到namespace映射的Mapper的二级缓存中。