Mybatis除了ORM各种SQL操作外,还提供了一个强大的查询缓存功能,它可以非常方便的配置和定制缓存,可以极大的提升查询效率
Mybatis默认定义了两级缓存:
- 一级缓存(本地缓存):默认情况下只有一级缓存开启
- 二级缓存(全局缓存):它需要手动开启和配置,它是基于namespace基本的缓存
- 对于二级缓存,Mybatis为了提供拓展性定义了一个Cache缓存接口,用户可以提供实现Cache接口自定义二级缓存,官方也提供redis,ehcache等缓存框架来整合二级缓存
一,一级缓存
一,一级缓存的使用
一级缓存是Mybatis默认就开启的
一级缓存:与数据库同一次会话期间查询到的数据会放入本地缓存中,以后如果需要获取相同的数据直接从本地缓存中取,不需要查询数据库
一级缓存的命中条件:
- 同一个SqlSession会话
- 同样的查询条件
简单使用一级缓存:mapper接口和pojo这里省略
① 测试一级缓存:同一个sqlsession和同样的查询语句
② 结果
二,一级缓存失效的四种情况
一级缓存失效可以从命中条件反推它的失效条件
四种失效情况:
- 使用的SqlSession对象不同
- SqlSession对象相同,查询条件不同
- 两次查询期间执行了增删改操作
- 手动清除了一级缓存
注:上述条件一个出现都不会触发一级缓存
一,使用不同的SqlSession对象
① 测试
public class test3 { public static void main(String[] args) throws IOException { //读取主配置文件 InputStream stream= Resources.getResourceAsStream("mybatisconfig.xml"); //Sqlsession工厂创建器 SqlSessionFactoryBuilder sessionFactoryBuilder= new SqlSessionFactoryBuilder(); //创建SqlSession工厂 SqlSessionFactory sqlSessionFactory=sessionFactoryBuilder.build(stream); //获取SqlSession1对象和SqlSession2对象 SqlSession session1=sqlSessionFactory.openSession(); SqlSession session2=sqlSessionFactory.openSession(); //通过SqlSession获取两个代理接口 Iwqluser iprople1= session1.getMapper(Iwqluser.class); Iwqluser iprople2= session2.getMapper(Iwqluser.class); //查询1 wqluser wqluser1 = iprople1.select2(1); System.out.println(wqluser1); //查询2 wqluser wqluser2 = iprople2.select2(1); System.out.println(wqluser2); //判断两个结果对象是否相对 System.out.println(wqluser1==wqluser2); session1.close(); session2.close(); }}
二,使用不同的查询条件
① 测试
public class test3 { public static void main(String[] args) throws IOException { //读取主配置文件 InputStream stream= Resources.getResourceAsStream("mybatisconfig.xml"); //Sqlsession工厂创建器 SqlSessionFactoryBuilder sessionFactoryBuilder= new SqlSessionFactoryBuilder(); //创建SqlSession工厂 SqlSessionFactory sqlSessionFactory=sessionFactoryBuilder.build(stream); //获取SqlSession1对象 SqlSession session1=sqlSessionFactory.openSession(); //通过SqlSession获取代理接口 Iwqluser iprople1= session1.getMapper(Iwqluser.class); //查询1 wqluser wqluser1 = iprople1.select2(1); System.out.println(wqluser1); //查询2 wqluser wqluser2 = iprople1.select2(2); System.out.println(wqluser2); //判断两个结果对象是否相对 System.out.println(wqluser1==wqluser2); session1.close();}}
② 结果
三,两次查询期间执行了增删改操作
① 测试
public class test3 { public static void main(String[] args) throws IOException { //读取主配置文件 InputStream stream= Resources.getResourceAsStream("mybatisconfig.xml"); //Sqlsession工厂创建器 SqlSessionFactoryBuilder sessionFactoryBuilder= new SqlSessionFactoryBuilder(); //创建SqlSession工厂 SqlSessionFactory sqlSessionFactory=sessionFactoryBuilder.build(stream); //获取SqlSession1对象 SqlSession session1=sqlSessionFactory.openSession(); //通过SqlSession获取代理接口 Iwqluser iprople1= session1.getMapper(Iwqluser.class); //查询1 wqluser wqluser1 = iprople1.select2(1); System.out.println(wqluser1); //执行一个插入操作 prople wqlusers = new prople(); wqlusers.setId(5); wqlusers.setName("FQWQL"); wqlusers.setAge(10); wqlusers.setBmid(1); wqlusers.setXb("男"); iprople1.insert(wqlusers); //查询2 wqluser wqluser2 = iprople1.select2(1); System.out.println(wqluser2); //判断两个结果对象是否相对 System.out.println(wqluser1==wqluser2); session1.close(); } }
② 结果
四,手动清除缓存
① 测试
public class test3 { public static void main(String[] args) throws IOException { //读取主配置文件 InputStream stream= Resources.getResourceAsStream("mybatisconfig.xml"); //Sqlsession工厂创建器 SqlSessionFactoryBuilder sessionFactoryBuilder= new SqlSessionFactoryBuilder(); //创建SqlSession工厂 SqlSessionFactory sqlSessionFactory=sessionFactoryBuilder.build(stream); //获取SqlSession1对象 SqlSession session1=sqlSessionFactory.openSession(); //通过SqlSession获取代理接口 Iwqluser iprople1= session1.getMapper(Iwqluser.class); //查询1 wqluser wqluser1 = iprople1.select2(1); System.out.println(wqluser1); //清空一级缓存 session1.clearCache(); //查询2 wqluser wqluser2 = iprople1.select2(1); System.out.println(wqluser2); //判断两个结果对象是否相对 System.out.println(wqluser1==wqluser2); session1.close(); } }
② 结果
二,二级缓存
二级缓存是基于namespace(xml映射文件)的缓存,一个namespace对应一个二级缓存
工作机制:
- 一个SqlSession会话,查询一条数据,这个数据会被放在一级缓存中
- 如果会话关闭,一级缓存中的数据并没有清空而是会被保存到二级缓存中,新的会话查询信息也参照二级缓存
- 不同的namespace查出的数据会放在自己对应的缓存中
- Mybatis自带的二级缓存本质上是一个Map集合,但它提供了一个Cache接口可以整合第三方缓存如Redis,MenCache,EhCache
一,二级缓存的使用
二级缓存使用:
- 在主配置文件中开启二级缓存
- 在具体的xml映射文件使用二级缓存
注:在主配置文件中开启二级缓存,如果在具体的映射文件中没有设置缓存,缓存也不会生效
① 在主配置文件中开启二级缓存
<settings> <!--开启二级缓存,默认其实也是开启的,但一般开发中,开启的配置需要指定--> <setting name="cacheEnabled" value="true"/> </settings>
② 在映射文件中设置二级缓存
<!--在映射文件中设置这个标签就表明开启了,但它还要参数,默认可以不设置使用默认值--> <cache></cache>
③ pojo类需要实现序列化接口
cache标签的参数:
1,eviction:缓存的回收策略
- LRU:最近最少使用的,移除最长时间不被使用的对象(默认)
- FIFO:先进先出,按照对象进入缓存的顺序来移除它们
- SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象
- WEAK:弱引用,更积极的移除基于垃圾收集器状态和弱引用规则的对象
2,FlushInterval:缓存刷新间隔,缓存多长时间清空一次,默认不清空,值为毫秒
3,readOnly:是否只读
- true:只读,mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据,追踪方式Mybatis会直接将数据的真实引用交给用户,假如用户修改了数据就不安全但速度快
- false:非只读,mybatis觉得获取的数据会被修改,会利用序列号和反序列号克隆数据给用户,这种方式安全但速度不快(默认)
4,size:缓存存放多少数据
5,type:指定自定义缓存的全类名,这个是缓存的一个拓展,它提供了一个Cache接口给用户可以自定义缓存,假如实现了自定义缓存把全类名填进去即可
注:查出的数据都会默认放在一级缓存中,只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中,因此在使用二级缓存时,必须将一级缓存的SqlSession提交或者关闭
例:
① 测试
public class test3 { public static void main(String[] args) throws IOException { //读取主配置文件 InputStream stream= Resources.getResourceAsStream("mybatisconfig.xml"); //Sqlsession工厂创建器 SqlSessionFactoryBuilder sessionFactoryBuilder= new SqlSessionFactoryBuilder(); //创建SqlSession工厂 SqlSessionFactory sqlSessionFactory=sessionFactoryBuilder.build(stream); //获取SqlSession1对象和SqlSession2对象 SqlSession session1=sqlSessionFactory.openSession(); SqlSession session2=sqlSessionFactory.openSession(); //通过SqlSession获取两个代理接口 Iwqluser iprople1= session1.getMapper(Iwqluser.class); Iwqluser iprople2= session2.getMapper(Iwqluser.class); //查询1 wqluser wqluser1 = iprople1.select2(1); System.out.println(wqluser1); //关闭session1会话,让该缓存中的数据进入到二级缓存中 session1.close(); //查询2 wqluser wqluser2 = iprople2.select2(1); System.out.println(wqluser2); //判断两个结果对象是否相对 System.out.println(wqluser1==wqluser2); session2.close(); } }
② 结果
三,缓存的相关属性和设置
-
默认为true使用缓存
-
false:不使用缓存,但这里只对二级缓存有效
<select id="select1" resultMap="wql" useCache="true"> select prople.id,bm.bmname,prople.name,prople.age,prople.xb,prople.bmid from prople left join bm on prople.bmid=bm.id </select>
-
默认为ture,每执行一次就会清除缓存,这就是为什么执行增删改操作,一级缓存会失效的原因
-
这个清除缓存,对一级缓存和二级缓存都有效
<insert id="insert" parameterType="com.fq.prople" flushCache="true"> insert into prople (id,name,age,xb,bmid) values (#{id},#{name},#{age},#{xb},#{bmid}) </insert>
-
每一次查询之前都会清空缓存,那么一二级缓存都是无效的
四,缓存机制的执行流程
缓存执行的总体流程:
- 优先二级缓存
- 一级缓存
- 真实查询
五,Mybatis整合第三方缓存
Mybatis的一级缓存合二级缓存本质上都是保存在内存中的Map集合,Mybatis的缓存实现了Cache接口
一,Cache接口
Cache是缓存实现的顶级接口,Mybatis本地缓存(一级缓存),全局缓存(无第三方整合)都对Cache做了实现
整合第三方缓存也是使用这个接口,Myabtis提供了整合第三方缓存的适配包,如:Ehcache,Memcache,Redis……,这些适配包无一例外都实现了Cache接口
Cache接口的源码:
public class PerpetualCache implements Cache { //设置当前Map的唯一ID private final String id; //Map集合,保存缓存数据 private Map<Object, Object> cache = new HashMap(); public PerpetualCache(String id) { this.id = id; } public String getId() { return this.id; } //设置缓存的大小 public int getSize() { return this.cache.size(); } //put数据到缓存Map public void putObject(Object key, Object value) { this.cache.put(key, value); } //获取cache数据 public Object getObject(Object key) { return this.cache.get(key); } //移除cache数据 public Object removeObject(Object key) { return this.cache.remove(key); } //清空缓存,本质上就是清空Map public void clear() { this.cache.clear(); } //获取读写锁 public ReadWriteLock getReadWriteLock() { return null; } public boolean equals(Object o) { if (this.getId() == null) { throw new CacheException("Cache instances require an ID."); } else if (this == o) { return true; } else if (!(o instanceof Cache)) { return false; } else { Cache otherCache = (Cache)o; return this.getId().equals(otherCache.getId()); } } public int hashCode() { if (this.getId() == null) { throw new CacheException("Cache instances require an ID."); } else { return this.getId().hashCode(); } } }
Cache接口的实现类:
- SoftCache:软引用类型Cache
- LruCache:Lru类型的Cache
- PerprtualCache:默认一级缓存使用的Cache
- WeakCache:弱引用类型的Cache
- FifoCache:先进先出类型的Cache
- ……
二,整合Redis第三方缓存
步骤:
- 项目整合并配置Redis
- 引入Mybatis-redis适配包
- 在映射Map文件中配置Cache标签的type,指定为第三方缓存,这个类适配包会提供
适配包Github地址(Mybatis作者开发):https://github.com/harawata?tab=repositories
整合Redis为第三方缓存的文档:http://mybatis.org/redis-cache/
redis缓存适配包maven坐标:
<dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-redis</artifactId> <version>1.0.0-beta2</version> </dependency>
整合演示:
1,配置redis(配置不做解释)
① 导入redis和spring整合依赖
<dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>2.5.3</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.6.3</version> </dependency>
② 配置Redis
<!-- 1、配置连接池信息 --> <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <!-- 最大连接数--> <property name="maxTotal" value="50"></property> <property name="maxIdle" value="5"></property> </bean> <!--2、spring整合Jedis(Redis) 也就是配置连接工厂JedisConnectionFactory--> <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <!--需要自定义(指定)一些工厂属性配置信息--> <!-- 指定服务器地址--> <property name="hostName" value="127.0.0.1"></property> <!-- 指定服务端口号--> <property name="port" value="6379"/> <!-- 指定密码(Redis3之前可以不指定,之后都要)--> <property name="password" value="yichun"/> <!-- 自定义连接池配置:再把第一步配置好的连接池信息通过属性注入进来 如果不自定义会采用默认的连接池配置,工厂中有属性new JedisPoolConfig--> <property name="poolConfig" ref="jedisPoolConfig"></property> </bean> <!-- 3、配置RedisTemplate模板 把第二步配置好的连接工厂JedisConnectionFactory通过属性注入到RedisTemplate模板中--> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory" ref="jedisConnectionFactory"></property> <!--如下配置是为了之后的key和value的序列化操作,暂时不配制也是OK的--> <property name="keySerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"></bean> </property> <property name="valueSerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"></bean> </property> </bean>
2,导入适配器依赖(上面有)
3,在映射文件设置cache的类型为redis
<mapper namespace="org.acme.FooMapper"> <cache type="org.mybatis.caches.redis.RedisCache" /> </mapper>
Comments | NOTHING
Warning: Undefined variable $return_smiles in /www/wwwroot/wql_luoqin_ltd/wp-content/themes/Sakura/functions.php on line 1109