MyBatis缓存介绍
为什么要用缓存?
在首次访问时,查询数据库,将查询的数据库信息存入内存中;再次访问时可直接访问缓存,以减少IO、硬盘读写次数、提高效率。
MyBatis的一级缓存和二级缓存
1.一级缓存
一级缓存是mybatis中的SqlSession对象的缓存。当我们执行完查询之后,查询的结果会同时存在在SqlSession为我们提供的一块区域中。当我们再次查询同样的数据,mybatis会先去SqlSession中查询是否有,有的话直接拿出来使用。当SqlSession对象消失时,Mybatis的一级缓存也就消失了。
2.二级缓存
二级缓存是Mybatis中SqlSessionFactory对象的缓存,由同一个SqlSessioFactory对象创建的SqlSession共享其缓存。
我们可以通过案例来解释一级缓存和二级缓存。
MyBatis一级缓存案例
首先我们需要创建好类和Mapper以方便接下来的测试
Pojo
package com.by.pojo;
import java.io.Serializable;
import java.util.Date;
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
}
Mapper
package com.by.mapper;
import com.by.pojo.User;
import java.util.List;
public interface UserMapper {
public User findById(int id);
void deleteById(Integer id);
}
xml:
<?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.by.mapper.UserMapper">
<cache></cache>
<select id="findById" resultType="com.by.pojo.User">
select * from user where id =#{id};
</select>
<delete id="deleteById" parameterType="int">
delete from user where id=#{id}
</delete>
</mapper>
Test
test1
@Test
public void testCache(){
System.out.println("=========第一次输出========");
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = userMapper.findById(42);
System.out.println(user);
System.out.println("=========第二次输出========");
User user2=userMapper.findById(42);
System.out.println(user2);
}
运行结果:
用同一个SqlSession创建的mapper第二次调用会直接读取缓存中的结果。
test2
@Test
public void testCache2(){
SqlSession session1 = sessionFactory.openSession();
SqlSession session2= sessionFactory.openSession();
UserMapper userMapper1 = session1.getMapper(UserMapper.class);
UserMapper userMapper2 = session2.getMapper(UserMapper.class);
System.out.println("=============第一次输出=============");
User user1 = userMapper1.findById(43);
System.out.println(user1);
System.out.println("=============修改一次===============");
userMapper1.deleteById(89);
System.out.println("=============第二次输出==============");
User user2 = userMapper2.findById(43);
System.out.println(user2);
}
运行结果:
当使用不同的SqlSeesion或者在中间进行修改、添加、删除操作时会重新加载缓存。
一级缓存案例分析
-
第一次发起查询用户id为 1 的用户信息,先去找缓存中是否有id为 1 的用户信息,如果没有,从数据库查询用户信息。 得到用户信息,将用户信息存储到一级缓存中。
-
如果sqlSession去执行 commit操作(执行插入、更新、删除),清空 SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
-
第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。
MyBatis二级缓存案例
Mapper.xml
<?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.by.mapper.UserMapper">
<cache></cache>
<select id="findById" resultType="com.by.pojo.User">
select * from user where id =#{id};
</select>
<delete id="deleteById" parameterType="int">
delete from user where id=#{id}
</delete>
</mapper>
Test
test1
@Test
public void testCache(){
SqlSession session1 = sessionFactory.openSession();
SqlSession session2 = sessionFactory.openSession();
UserMapper userMapper1 = session1.getMapper(UserMapper.class);
UserMapper userMapper2 = session2.getMapper(UserMapper.class);
System.out.println("===============第一次查询================");
User user1 = userMapper1.findById(41);
System.out.println(user1);
session1.close();
System.out.println("===============第二次查询================");
User user2 = userMapper2.findById(41);
System.out.println(user2);
}
运行结果:
当利用一个sqlSessionFactory创建多个sqlSession并操作同一个查询语句时, 多个sqlSession可共用二级缓存。
test2
@Test
public void testNoCache2() throws IOException {
InputStream is1 = Resources.getResourceAsStream("mybatis-config.xml");
InputStream is2 = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sessionFactory1 = new SqlSessionFactoryBuilder().build(is1);
SqlSessionFactory sessionFactory2 = new SqlSessionFactoryBuilder().build(is2);
SqlSession session1 = sessionFactory1.openSession();
SqlSession session2 = sessionFactory2.openSession();
UserMapper userMapper1 = session1.getMapper(UserMapper.class);
UserMapper userMapper2 = session2.getMapper(UserMapper.class);
System.out.println("============第一次查询===============");
User user1 = userMapper1.findById(43);
System.out.println(user1);
session1.close();
System.out.println("=============删除====================");
userMapper2.deleteById(79);
System.out.println("==============第二次查询===============");
User user2 = userMapper2.findById(43);
System.out.println(user2);
}
运行结果:
和一级缓存差不多的是:当创建不同的sqlSessionFactory创建session或者查询过程中对其进行修改、添加、删除时都会重新读取。
二级缓存案例分析:
二级缓存是mapper映射级别的缓存,多个SqlSession去操作同一个Mapper映射的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
一级缓存和二级缓存的区别
一级缓存:
范围:sqlSession
配置:默认开启
走缓存:同一个sqlSession
不走缓存:不同的sqlSession或者两次查询中间执行了增删改
二级缓存:
范围:sqlSessionFactory
配置:<cache></cache>
走缓存:同一个sqlSessionFactory
不走缓存:不同的sqlSessionFactory或者两次查询中间执行了增删改