概念
- 缓存:存于内存中的临时数据
- 使用缓存优点:减少和数据库的交互次数,提高执行效率。
- 缓存适用于:经常查询并且不经常改变的、正确与否对最终结果影响不大的数据。
- 缓存不适用于: 经常改变的、正确与否对最终结果影响很大的。如:商品的库存,银行的汇率,股市的价格。
实例
Mybatis中的一级缓存
Mybatis中的 一级缓存,指的是 SqlSession对象的缓存。
当我们执行查询之后,查询的结果会同时存入SqlSession为我们提供一块区域中,该区域的结构是一个Map。
当我们再次查询同样的数据,mybatis会先去sqlsession中查询是否有此数据,有则直接取用。
当SqlSession对象消失时,mybatis的一级缓存也就消失了。
本例的目录结构:
mysql中的user表:
UserTest.java:
/**
* User测试类
*/
public class UserTest {
private InputStream in;
private SqlSessionFactory factory;
private SqlSession sqlSession;
private IUserDao userDao;
@Before//用于在测试方法执行之前执行
public void init()throws Exception{
//1.读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.获取SqlSessionFactory
factory = new SqlSessionFactoryBuilder().build(in);
//3.获取SqlSession对象
sqlSession = factory.openSession(true);
//4.获取dao的代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}
@After//用于在测试方法执行之后执行
public void destroy()throws Exception{
//提交事务
// sqlSession.commit();
//6.释放资源
sqlSession.close();
in.close();
}
/**
* 测试一级缓存
*/
@Test
public void testFirstLevelCache(){
User user1 = userDao.findById(6);
System.out.println(user1);
User user2 = userDao.findById(6);
System.out.println(user2);
System.out.println(user1 == user2);
}
执行testFirstLevelCache()方法,测试结果如下:
可见两次查询的结果是同一个对象,说明使用了缓存。
在测试代码中查询后,关闭sqlSession并再次获取,之后再查询一次:
/**
* 测试一级缓存
*/
@Test
public void testFirstLevelCache(){
User user1 = userDao.findById(6);
System.out.println(user1);
sqlSession.close();
// 再次获取SqlSession对象
sqlSession = factory.openSession();
userDao = sqlSession.getMapper(IUserDao.class);
User user2 = userDao.findById(6);
System.out.println(user2);
System.out.println(user1 == user2);
}
测试结果表明两次查询获取的对象不是同一个,且获取了两次SqlSession对象,说明SqlSession对象消失时,mybatis的一级缓存也就消失了,毕竟这种缓存是依赖于SqlSession对象的。
此外,SqlSession对象的clearCache()方法也可以清除缓存。
清空一级缓存的时机
一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除, commit(), close()等方法时,就会清空一级缓存。这样做的目的是,为了让缓存中存储的是最新的信息,避免脏读。
例如,IUserDAO中添加更新记录的方法:
/**
* 更新用户信息
* @param user
*/
void updateUser(User user);
并配置映射(IUserDAO.xml):
<!-- 更新用户信息-->
<update id="updateUser" parameterType="user">
update user set username=#{username},address=#{address} where id=#{id}
</update>
添加测试方法:
/**
* 测试缓存的同步
*/
@Test
public void testClearlCache(){
//1.根据id查询用户
User user1 = userDao.findById(6);
System.out.println(user1);
//2.更新用户信息
user1.setUsername("男林志玲");
user1.setAddress("香港");
userDao.updateUser(user1);
//3.再次查询id为6的用户
User user2 = userDao.findById(6);
System.out.println(user2);
System.out.println(user1 == user2);
}
测试结果:
可以看到,SqlSession执行了update后,当需要查询用户时并没有从缓存中取,而是又进行了一次查询,因为update后缓存已经被清空了。
Mybatis中的二级缓存
Mybatis中的二级缓存指的是 SqlSessionFactory对象的缓存——由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
二级缓存的使用步骤:
- 让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置setting的cacheEnabled属性)
- 让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
- 让当前的statement支持二级缓存(在select标签中配置)
下面按部就班进行二级缓存的配置——
- SqlMapConfig.xml:
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置,为 true 代表开启二级缓存;为
false 代表不开启二级缓存。
- IUserDao.xml:
<mapper namespace="com.zhu.dao.IUserDao">
<!--开启user支持二级缓存-->
<cache/>
......
</mapper>
<cache>标签表示当前这个 mapper 映射将使用二级缓存。
- IUserDao.xml 的 statement 上面的 useCache属性:
<!-- 根据id查询用户 -->
<select id="findById" parameterType="INT" resultType="user" useCache="true">
select * from user where id = #{uid}
</select>
准备就绪,新建一个测试类:
public class SecondLevelCacheTest {
private InputStream in;
private SqlSessionFactory factory;
@Before//用于在测试方法执行之前执行
public void init()throws Exception{
//1.读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.获取SqlSessionFactory
factory = new SqlSessionFactoryBuilder().build(in);
}
@After//用于在测试方法执行之后执行
public void destroy()throws Exception{
in.close();
}
/**
* 测试一级缓存
*/
@Test
public void testFirstLevelCache(){
SqlSession sqlSession1 = factory.openSession();
IUserDao dao1 = sqlSession1.getMapper(IUserDao.class);
User user1 = dao1.findById(6);
System.out.println(user1);
// 一级缓存消失
sqlSession1.close();
SqlSession sqlSession2 = factory.openSession();
IUserDao dao2 = sqlSession2.getMapper(IUserDao.class);
User user2 = dao2.findById(6);
System.out.println(user2);
sqlSession2.close();
System.out.println(user1 == user2);
}
}
测试结果:
从结果中可以看到,虽然有两个不同的sqlSession执行查询,但是stament只执行了一次,说明sqlSession2的查询使用的是缓存中的数据。此外,两次查询的结果对象依然是不同的,说明二级缓存的是数据而不是对象,当查询语句一致时,将从缓存中把数据填充到新的对象中。
当我们在使用二级缓存时,所缓存的类一定要实现 java.io.Serializable 接口,这样就可以使用序列化
方式来保存对象
总结
- Mybatis中的 一级缓存,指的是 SqlSession对象 的缓存
- Mybatis中的二级缓存,指的是 SqlSessionFactory对象的缓存,且需要在配置中开启缓存。