目录
十三、Mybatis 缓存
13.1 简介
像大多数的持久化框架一样,Mybatis也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提高性能。
Mybatis中缓存分为一级缓存,二级缓存
一级缓存:是SqlSession级别的缓存(线程级别的缓存)
二级缓存:是SqlSessionFactory级别的缓存(进程级别的缓存)
一个SqlSessionFactory存在多个SqlSession。
13.2 一级缓存
13.2.1 一级缓存的简介
一级缓存指的是Mybatis中SqlSession对象的缓存。
当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供一块区域中。
该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中查询是否有,有的话直接拿出来用,如果没有再查询数据库。
当SqlSession对象消失时,mybatis的一级缓存也就消失了。
13.2.2 测试一级缓存
User.java
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
// set、get方法
}
UserDao.java
public User findById(Integer id);
UserDao.xml
<select id="findById" resultType="user" parameterType="int">
select * from user where id = #{id}
</select>
MybatisTest.java
@Test
public void findById(){
User user1 = userDao.findById(48);
System.out.println(user1);
User user2 = userDao.findById(48);
System.out.println(user2);
System.out.println(user1 == user2);
}
在测试类中,我们在一个sqlSession中调用了两次 findById() 方法,并将它们的地址输出(这时要把User类中的toString方法去掉),来比较它们是否为一个对象,也意味着sqlSession对象是否对查询结果进行了存储。
结果显示:
通过结果,我们可以清楚地看到两个对象的地址完全相同,并且查询操作只进行了一次。
13.2.3 一级缓存的改进测试
这里我们在测试方法中手动关闭sqlSession,然后新建一个sqlSession来第二次调用查询方法,并且进行地址值的比较,来进一步确定sqlSession是一级缓存的载体。
MybatisTest.java
@Test
public void findById(){
User user1 = userDao.findById(48);
System.out.println(user1);
sqlSession.close();
sqlSession = sqlSessionFactory.openSession();
userDao = sqlSession.getMapper(UserDao.class);
User user2 = userDao.findById(48);
System.out.println(user2);
System.out.println(user1 == user2);
}
结果显示:
结果显而易见,在我们新建了一个sqlSession后,两次调用方法返回的 User 对象是两个不同的对象,由此证明了一级缓存就是sqlSession对象的缓存。
注意:一级缓存是SqlSession 范围的缓存,当调用SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。
13.3 二级缓存
13.3.1 简介
二级缓存指的是Mybatis中SqlSessionFactory对象的级别缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
二级缓存的使用步骤:
- 让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
- 让当前的映射文件支持二级缓存(在UserDao.xml中配置)
- 让当前的操作支持二级缓存(在select标签中配置)
13.3.2 二级缓存的开启与关闭
第一步:在 sqlMapConfig.xml 文件中开启二级缓存(cacheEnabled)
cacheEnabled 的取值默认就为true,所以这一步可以省略不配置。为true 代表开启二级缓存; 为false 代表不开启二级缓存 。
<!--全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。-->
<setting name="cacheEnabled" value="true"></setting>
第二步:配置UserDao.xml中相关的Mapper 映射文件 (cache)
<cache>标签表示当前这个 mapper 映射将使用二级缓存,区分的标准就看mapper 的namespace 值。
<mapper namespace="com.cpz.dao.UserDao">
<cache/>
</mapper>
第三步:配置UserDao.xml中statement 上面的useCache 属性 (在select标签中配置)
将 UserDao.xml 映射文件中的<select>标签中设置useCache=”true”代表当前这个 statement 要使用二级缓存,如果不使用二级缓存可以设置为false。
<select id="findById" resultType="user" parameterType="int" useCache="true">
select * from user where id = #{uid}
</select>
注意:针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存。
13.3.3 二级缓存的测试
MybatisTest.java
package com.cpz.test;
import com.cpz.dao.UserDao;
import com.cpz.domain.User;
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.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class MybatisTest {
InputStream is;
SqlSessionFactory sqlSessionFactory;
@Before
public void init() throws IOException {
is = Resources.getResourceAsStream("sqlMapConfig.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
}
@After
public void destroy() throws IOException {
is.close();
}
@Test
public void findById(){
SqlSession sqlSession1 = sqlSessionFactory.openSession();
UserDao userDao1 = sqlSession1.getMapper(UserDao.class);
User user1 = userDao1.findById(48);
System.out.println(user1);
sqlSession1.close(); // 一级缓存没有了
// 重新使用同一个sqlSessionFactory创建Session
SqlSession sqlSession2 = sqlSessionFactory.openSession();
UserDao userDao2 = sqlSession2.getMapper(UserDao.class);
User user2 = userDao2.findById(48);
System.out.println(user2);
System.out.println(user1 == user2);
sqlSession2.close();
}
}
这里我们使用同一个sqlSessionFactory工厂创建了两个不同的sqlSession对象,并分别用他们创建了userDao对象,调用了查询方法。
结果显示:
由图可知,查询语句只执行了一次,表示二级缓存确实发挥了作用,但两个对象的地址值不同,表明二级缓存中存放的是对象的散装数据,每次查询的时候需要重新封装实体对象。
13.3.4 二级缓存的应用场景
适应放置到二级缓存的数据:经常不会发生变化的数据,例如地区编码
不适合放置到二级缓存的数据:经常变化的数据,财务数据