Mybatis缓存
Mybatis缓存
像大多数的持久化框架一样,Mybatis 也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提
高性能。
Mybatis 中缓存分为一级缓存,二级缓存
1. Mybatis一级缓存
1.1 证明一级缓存的存在
一级缓存是session级别的缓存,只要sqlSession没有flush或者close,它就存在。无需配置
1.1.1 编写用户持久层接口
/**
* 用户的持久层接口
* Ceate By llb on 2019/8/5
*/
public interface UserMapper {
/**
* 根据id查询用户
* @return
*/
User findUserById(int id);
}
1.1.2 编写用户持久层映射文件
<?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.llb.dao.UserMapper">
<!--useCache-->
<select id="findUserById" resultType="user" parameterType="int" useCache="true">
select * from user where id = #{id}
</select>
</mapper>
1.1.3 编写测试方法
package com.llb.test;
/**
* Ceate By llb on 2019/8/5
*/
public class UserTest {
InputStream in = null;
UserMapper mapper = null;
SqlSession sqlSession = null;
SqlSessionFactory factory = null;
/**
* 在测试方法执行前执行
* @throws IOException
*/
@Before
public void init() throws IOException {
//1.读取配置文件,生成字节流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.获取sqlSessionFactory对象
factory = new SqlSessionFactoryBuilder().build(in);
//3.获取sqlSession对象
sqlSession = factory.openSession();
//4.获取dao的代理对象
mapper = sqlSession.getMapper(UserMapper.class);
}
/**
* 测试方法执行后执行
* @throws IOException
*/
@After
public void destory() throws IOException {
sqlSession.commit();
//6.释放资源
sqlSession.close();
in.close();
}
/**
* 根据id查询用户,sqlSession一级缓存:
* 当调用sqlSession的修改、添加、删除,commit() ,close(),clear()等方法时,会清空缓存
*/
@Test
public void findUserById(){
User user = mapper.findUserById(41);
User user2 = mapper.findUserById(41);
System.out.println(user == user2);
}
}
测试结果:
我们虽然查询了两次,但最后只查询了一次数据库,这就是Mybatis提供给我们得一级缓存起作用。因为一级缓存的存在,导致第二次查询id为41的记录时,并没有发出sql语句从数据库中查询数据,而是从一级缓存中查询。
1.1.2 一级缓存的分析
一级缓存是SqlSession范围的缓存,当调用SqlSession的修改、添加、删除时、commit、close等方法就会清空缓存。
第一次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,如果没有,从数据库查询用户信息。
得到用户信息,将用户信息存储到一级缓存中。
如果 sqlSession 去执行 commit 操作(执行插入、更新、删除),清空 SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
第二次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,缓存中有,直接从缓存中获取用户信息。
2.1.3 测试一级缓存的清空
/**
* 根据id查询用户,sqlSession一级缓存:
* 当调用sqlSession的修改、添加、删除,commit() ,close(),clear()等方法时,会清空缓存
*/
@Test
public void findUserById(){
User user = mapper.findUserById(41);
//关闭sqlSession会清空缓存
sqlSession.close();
sqlSession = factory.openSession();
mapper = sqlSession.getMapper(UserMapper.class);
//直接对sqlSession进行清空
// sqlSession.clearCache();
User user2 = mapper.findUserById(41);
System.out.println(user == user2);
}
当执行sqlSession.close()后,再次获取sqlSession并查询id=41的User对象时,又重新执行了sql 语句,从数据库进行了查询操作。导致两个用户的地址不一致。
2. Mybatis二级缓存(XML方式)
二级缓存是mapper映射级别的缓存,多个sqlSession去操作同一个mapper映射的sql语句,多个sqlSession可以共用二级缓存,二级缓存是跨sqlSession的。
2.1 二级缓存的结构图
首先开启Mybatis的二级缓存。
sqlSession1去查询用户信息,查询到用户信息会将查询数据存储到二级缓存中。
当sqlSession2去查询同个用户信息时,首先会去二级缓存中查找是否存在数据,如果存在直接从缓存中取出数据。
如果sqlSession3去执行相同mapper映射下sql,执行commit提交,将会清空该mapper映射下的二级缓存区域的数据。
2.2 二级缓存的开启与关闭
2.2.1 第一步:在SqlMapConfig.xml文件开启二级缓存
<!--开启二级缓存,默认值为true,默认开启-->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
2.2.2 第二部:配置相关的Mapper映射文件
<cache>标签表示当前这个mapper映射将使用二级缓存,区分就看namespace的值
<?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.llb.dao.UserMapper">
<!--开启user支持二级缓存-->
<cache/>
</mapper>
2.2.3 第三步:配置statement上面的useCache属性
Mybatis缓存
<?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.llb.dao.UserMapper"> <!--开启user支持二级缓存--> <cache/> <!--useCache,对该方法使用二级缓存--> <select id="findUserById" resultType="user" parameterType="int" useCache="true"> select * from user where id = #{id} </select> </mapper>
注意:针对每次查询都需要最新数据的sql,要设置成useCache=false,禁用二级缓存。
2.2.3 二级缓存测试
package com.llb.test;
/**
* Ceate By llb on 2019/8/5
*/
public class SecondLevelCache {
InputStream in = null;
SqlSessionFactory factory = null;
/**
* 在测试方法执行前执行
* @throws IOException
*/
@Before
public void init() throws IOException {
//1.读取配置文件,生成字节流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.获取sqlSessionFactory对象
factory = new SqlSessionFactoryBuilder().build(in);
}
/**
* 测试方法执行后执行
* @throws IOException
*/
@After
public void destory() throws IOException {
in.close();
}
/**
* 根据id查询用户,二级缓存:
* 存放的是数据,而不是对象
*/
@Test
public void findUserById(){
SqlSession sqlSession = factory.openSession();
UserMapper mapper1 = sqlSession.getMapper(UserMapper.class);
User user1 = mapper1.findUserById(41);
System.out.println(user1);
//关闭缓存
sqlSession.close();
SqlSession sqlSession2 = factory.openSession();
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
User user2 = mapper2.findUserById(41);
System.out.println(user2);
System.out.println(user1 == user2);
}
}
经过上面测试,我们发现执行了两次查询,并且在第一次查询后关闭了一级缓存,再去执行二级缓存时,我们发现并没有对数据库发出SQL语句,所以此时的数据就只能来自二级缓存
2.2.4 二级缓存注意事项
当我们在使用二级缓存时,所缓存的类一定要实现java.io.Serializable接口,这种就可以使用序列化方式保存对象
package com.llb.domain;
import java.io.Serializable;
/**
* Ceate By llb on 2019/8/5
*/
public class User implements Serializable{
private Integer id;
private String username;
private String address;
private String sex;
private Date birthday;
}
3. 使用注解实现二级缓存
3.1 在SqlMapConfig中开启二级缓存支持
<!-- 配置二级缓存 -->
<settings> <!-- 开启二级缓存的支持 -->
<setting name="cacheEnabled" value="true"/> </settings>
3.2 在持久层接口中使用注解配置二级缓存
package com.llb.dao;
import java.util.List;
//开启二级缓存
@CacheNamespace(blocking = true)
public interface UserMapper {
}
MyBatis注解开发
github:https://github.com/PopsiCola/SSM-mybatis/tree/cache
欢迎star~
作者:PopsiCola
邮箱:liulebinn@163.com
出处:https://www.cnblogs.com/liulebin/
github:https://github.com/PopsiCola 欢迎star~
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。