1. 什么是查询缓存
1. Mybatis提供了查询缓存的功能,用于减轻数据库查询压力,分别提供了一级缓存和二级缓存两种缓存级别。
2. 查询缓存,就是将SQL查询语句查询的结果缓存中内存中(通过HashMap保存),如果多次执行同一个查询语句,就不需要每次都与数据库进行一次会话(创建SqlSeesion对象发送SQL语句),而是直接从内存中取出查询的结果,这样就可以提高性能,减少数据库压力。
3. 一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。
4. 二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
2. 一级缓存
1. 工作原理:以查询用户为例,
- 第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。
- 如果sqlSession去执行commit操作(即执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
- 第二次发起查询用户id为1的用户信息(SQL语句相同,传入的参数也相同),先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。
2. Mybatis默认开启一级缓存,所以不需要进行配置操作。
3. 应用:
在正式开发中,是将mybatis和spring进行整合开发,事务控制在service中。
一个service方法中包括 很多mapper方法调用。
service(){
//开始执行时,开启事务,创建SqlSession对象
//第一次调用mapper的方法findUserById(1)
//第二次调用mapper的方法findUserById(1),从一级缓存中取数据
//方法结束,sqlSession关闭
}
如果是执行两次service调用查询相同 的用户信息,不走一级缓存,因为session方法结束,sqlSession就关闭,一级缓存就清空。
3. 二级缓存
1. 工作原理:首先要开启二级缓存
- sqlSession1去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到二级缓存中。
- 如果SqlSession3去执行相同 mapper下sql,执行commit提交,清空该 mapper下的二级缓存区域的数据。
- sqlSession2去查询用户id为1的用户信息,去缓存中找是否存在数据,如果存在直接从缓存中取出数据。
2. 二级缓存与一级缓存区别:二级缓存的范围更大,多个sqlSession可以共享一个UserMapper的二级缓存区域;其次二级缓存的位置不一定是内存中,也有可能是硬盘或者其他缓存介质。UserMapper有一个二级缓存区域(按namespace分) ,其它mapper也有自己的二级缓存区域(按namespace分)。每一个namespace的mapper都有一个二缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同 的二级缓存区域中。
3. 配置开启二级缓存:首先在Mybatis的启动配置文件SqlMapConfig.xml中加入<setting name="cacheEnabled" value="true"/> ,然后在要开启二级缓存的Mapper映射文件中添加<cache></cache> 标签
<!--启动配置文件-->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
<environments default="environment">
<environment id="environment">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://localhost:3306/cloud_note?useUnicode=true&characterEncoding=utf8" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<!--指定映射文件位置-->
<mappers>
<mapper resource="mapper/Usermapper.xml"/>
</mappers>
</configuration>
<!--Mapper文件配置-->
<?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="dao.UserDao">
<!--开启二级缓存-->
<cache></cache>
<select id="findUserById" parameterType="string" resultType="entity.User">
select * from cn_user where cn_user_id=#{id}
</select>
</mapper>
4. 禁用某SQL查询语句的二级缓存:在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认值是true,即该sql使用二级缓存。<select id="find" resultMap="UserMap" useCache="false">;针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存。
5. 刷新缓存:在mapper的同一个namespace中,如果有其它insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。设置statement配置中的flushCache="true" 属性,默认值为true即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">,一般都应该设置为true
4. Mybatis与第三方缓存框架整合
1. Mybatis中的二级缓存不支持分布式缓存,要实现分布式缓存需要与第三方缓存工具进行整合使用,常用的比如Redis、ehcache等。
2. 整合原理:mybatis提供了一个Cache接口, 如果要实现自己的缓存逻辑,实现cache接口开发即可。
import java.util.concurrent.locks.ReadWriteLock;
import org.apache.ibatis.cache.Cache;
public class MyCache implements Cache{
@Override
public String getId() {
return null;
}
@Override
public void putObject(Object key, Object value) {
}
@Override
public Object getObject(Object key) {
return null;
}
@Override
public Object removeObject(Object key) {
return null;
}
@Override
public void clear() {
}
@Override
public int getSize() {
return 0;
}
@Override
public ReadWriteLock getReadWriteLock() {
return null;
}
}
Mybatis默认使用的Cache实现类(实现二级缓存):使用HashMap实现
package org.apache.ibatis.cache.impl;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheException;
public class PerpetualCache implements Cache {
private String id;
private Map<Object, Object> cache = new HashMap<Object, Object>();
public PerpetualCache(String id) {
this.id = id;
}
public String getId() {
return id;
}
public int getSize() {
return cache.size();
}
public void putObject(Object key, Object value) {
cache.put(key, value);
}
public Object getObject(Object key) {
return cache.get(key);
}
public Object removeObject(Object key) {
return cache.remove(key);
}
public void clear() {
cache.clear();
}
public ReadWriteLock getReadWriteLock() {
return null;
}
public boolean equals(Object o) {
if (getId() == null) throw new CacheException("Cache instances require an ID.");
if (this == o) return true;
if (!(o instanceof Cache)) return false;
Cache otherCache = (Cache) o;
return getId().equals(otherCache.getId());
}
public int hashCode() {
if (getId() == null) throw new CacheException("Cache instances require an ID.");
return getId().hashCode();
}
}
除此之外提供了多种缓存策略的实现类:
3. 配置使用第三方缓存工具:以mybatis和ehcache整合包中提供了一个cache接口的实现类为例,
- 首先添加jar包
- 其次,只需要在Mapper映射文件中的<cache></cache> 标签中进行配置,通过type属性来指定当前Mapper的二级缓存使用的Cache实现类,同样的,如果有自己实现的二级缓存类,也通过type属性来指定成二级缓存使用的Cache实现类
4. 二级缓存应用场景:对于访问多的查询请求且用户对查询结果实时性要求不高,此时可采用mybatis二级缓存技术降低数据库访问量,提高访问速度,业务场景比如:耗时较高的统计分析sql、电话账单查询sql等。因为如果要求查询结果数据实时性高,那么仍然会频繁的与数据进行交互,二级缓存就是去了意义;通过设置刷新间隔时间,由mybatis每隔一段时间自动清空缓存,根据数据变化频率设置缓存刷新间隔flushInterval,比如设置为30分钟、60分钟、24小时等,根据需求而定,在Mapper映射文件中的<cache>标签指定flushInterval属性的值即可,如<cache flushInterval="6000"></cache>,单位为毫秒