搜索 K
Appearance
博客正在加载中...
Appearance
缓存,就是存在于内存中的临时数据。使用缓存,可以减少和数据库的交互次数,提高执行效率。
什么样的数据能使用缓存,什么样的数据不能使用?
适用于缓存的情况:经常查询并且不经常改变的,或数据的正确与否对最终结果影响不大的情况
不适用于缓存的情况:经常改变的数据,或数据的正确与否对最终结果影响很大的。例如:商品的库存,银行的汇率,股市的牌价。
Mybatis 中存在一级缓存和二级缓存。
一级缓存:它指的是 Mybatis 中 SqlSession 对象的缓存。
我们新建查询一个的方法:在 IUserDao.java 接口中新增:
/**
* 根据id查询用户信息
* @param userId
* @return
*/
User findById(Integer userId); 修改 IUserDao.xml,增加如下代码:
<!-- 根据ID查询用户 -->
<select id="findById" parameterType="INT" resultType="com.peterjxl.domain.User">
select * from user where id = #{uid}
</select>测试
@Test
public void testFirstLevelCache(){
User user = userDao.findById(41);
System.out.println("user: " + user);
User user2 = userDao.findById(41);
System.out.println("user2: " + user2);
System.out.println("user2 == user ? : " + (user2 == user));
} 我们试着运行,可以看到只查询了一次,并且两个对象是同一个:

而如果 Session 关闭了,那么缓存就会被清理:
@Test
public void testFirstLevelCache(){
User user = userDao.findById(41);
System.out.println("user: " + user);
session.close();
session = factory.openSession();
userDao = session.getMapper(IUserDao.class);
User user2 = userDao.findById(41);
System.out.println("user2: " + user2);
System.out.println("user2 == user ? : " + (user2 == user));
}运行结果:

除此之外,sqlSession 还存在一个 clearCache 的方法,可以清理缓存。
session.clearCache();
如果数据库的数据,和缓存里的数据不一致了,那么 Mybatis 是如何更新缓存的呢?我们来举个例子。 修改 IUserDao.java。增加 updateUser 方法
package com.peterjxl.dao;
import com.peterjxl.domain.User;
import java.util.List;
public interface IUserDao {
List<User> findAll();
void saveUser(User user);
void updateUser(User user);
}在 IUserDao.xml 里增加一个 update 标签
<update id="updateUser" parameterType="com.peterjxl.domain.User">
update user
set username=#{username}, address=#{address}, sex=#{sex}, birthday=#{birthday}
where id=#{id}
</update> 新增测试方法
@Test
public void testClearCache(){
// 1. 根据 id 查询用户信息
User user = userDao.findById(41);
System.out.println("user: " + user);
// 2. 更新用户信息
user.setUsername("update user clear cache");
user.setAddress("克莱登大学");
userDao.updateUser(user);
// 3. 再次查询id 为41的用户
User user2 = userDao.findById(41);
System.out.println("user2: " + user2);
System.out.println("user2 == user ? : " + (user2 == user));
}运行结果:可以看到发起了两次查询:
这是因为当调用 SqlSession 的修改,添加,删除,commit(),close() 等方法时,就会清空一级缓存。
二级缓存:指的是 Mybatis 中 SqlSessionFactory 对象的缓存。由同一个 SqlSessionFactory 对象创建的 SqlSession 共享其缓存。使用步骤:
这里我们将 SqlSession 和 IUserDao 对象,不初始化,也不单独定义成员变量:
package com.peterjxl.test;
import com.peterjxl.dao.IUserDao;
import com.peterjxl.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;
import java.util.List;
public class SecondLevelCacheTest {
private InputStream in;
private SqlSessionFactoryBuilder builder;
private SqlSessionFactory factory;
@Before
public void init() throws IOException {
in = Resources.getResourceAsStream("SqlMapConfig.xml");
builder = new SqlSessionFactoryBuilder();
factory = builder.build(in);
}
@After
public void destory() throws IOException {
in.close();
}
} 新增测试方法:
@Test
public void testSecondLevelCache(){
SqlSession sqlSession1 = factory.openSession();
IUserDao dao1 = sqlSession1.getMapper(IUserDao.class);
User user1 = dao1.findById(41);
System.out.println(user1);
sqlSession1.close();
SqlSession sqlSession2 = factory.openSession();
IUserDao dao2 = sqlSession2.getMapper(IUserDao.class);
User user2 = dao2.findById(41);
System.out.println(user2);
sqlSession2.close();
System.out.println("user1 == user2 ? : " + (user1 == user2));
} 可以看到还是查询了两次,这是因为我们还没配置二级缓存:

在官网文档:mybatis – MyBatis 3 | 配置 中,有提到如何开启缓存:
可以看到默认值为 true,其实不配置也行,这里就是为了让大伙知道这个配置
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>只需加一个 <cache/> 标签即可:
<?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.peterjxl.dao.IUserDao">
<cache/>
// 省略其他配置。。。。。
</mapper>在 select 标签里,新增属性 useCache="true"
<!-- 根据ID查询用户 -->
<select id="findById" parameterType="INT" resultType="com.peterjxl.domain.User" useCache="true">
select * from user where id = #{uid}
</select> 可以看到只查询了一次,并告诉我们命中了缓存(Cache Hit)

但是,两个对象却是不相等的:
System.out.println("user1 == user2 ? : " + (user1 == user2));这是因为二级缓存中,内容是数据,而不是对象。
所有代码已上传到了 GitHub 和 Gitee 上,并且创建了分支 demo18,读者可以通过切换分支来查看本文的示例代码。