一:一級緩存:
一級緩存在mybatis中默認是開啟的並且是session級別,它的作用域為一次sqlSession會話。
1 import com.smartdata.pms.PmsApplication; 2 import com.smartdata.pms.entity.PmsProduct; 3 import com.smartdata.pms.mapper.PmsProductMapper; 4 import com.smartdata.pms.service.PmsProductService; 5 import org.junit.Test; 6 import org.junit.runner.RunWith; 7 import org.springframework.beans.factory.annotation.Autowired; 8 import org.springframework.boot.test.context.SpringBootTest; 9 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 10 import org.springframework.transaction.annotation.Transactional; 11 12 import java.util.concurrent.LinkedBlockingQueue; 13 import java.util.concurrent.ThreadPoolExecutor; 14 import java.util.concurrent.TimeUnit; 15 16 /** 17 * @ProjectName: smartdata 18 * @Package: PACKAGE_NAME 19 * @ClassName: CacheTest 20 * @Author: heluwei 21 * @Description: 緩存測試 22 * @Date: 2020/3/21 18:34 23 * @Version: 1.0 24 */ 25 @RunWith(SpringJUnit4ClassRunner.class) 26 @SpringBootTest(classes = PmsApplication.class) 27 public class CacheTest{ 28 @Autowired 29 PmsProductMapper pmsProductMapper; 30 @Test 31 @Transactional(rollbackFor = Throwable.class) 32 public void testFistCache(){ 33 // 第一次查詢,緩存到一級緩存 34 System.out.println(pmsProductMapper.selectById(1)); 35 // 第二次查詢,直接讀取一級緩存 36 System.out.println(pmsProductMapper.selectById(1)); 37 38 } 39 }
為什么開啟事務
由於使用了數據庫連接池,默認每次查詢完之后自動commite,這就導致兩次查詢使用的不是同一個sqlSessioin,根據一級緩存的原理,它將永遠不會生效。
當我們開啟了事務,兩次查詢都在同一個sqlSession中,從而讓第二次查詢命中了一級緩存。讀者可以自行關閉事務驗證此結論。
二:二級緩存
默認情況下,mybatis打開了二級緩存,但它並未生效,因為二級緩存的作用域是namespace,所以還需要在Mapper.xml文件中配置一下才能使二級緩存生效
使用Redis實現Mybatis的二級緩存。因為Mybatis的二級緩存存放在本地內存中。
一:在yml文件中
1 #配置mapper 2 mybatis: 3 #開啟Mybatis的二級緩存 4 configuration: 5 cache-enabled: true
二:自定義MybatisRedisCache
package com.smartdata.pms.cache; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.cache.Cache; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.util.CollectionUtils; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * @ProjectName: smartdata * @Package: com.smartdata.pms.cache * @ClassName: MyDefineRedisCache * @Author: heluwei * @Description: 使用Redis緩存Mybatis的二級緩存 * @Date: 2020/3/22 22:56 * @Version: 1.0 */ @Slf4j public class MybatisRedisCache implements Cache { // 讀寫鎖 private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true); private static RedisTemplate redisTemplate; // private RedisTemplate<String, Object> redisTemplate = SpringContextHolder.getBean("redisTemplate"); private String id; public MybatisRedisCache(final String id) { if (id == null) { throw new IllegalArgumentException("Cache instances require an ID"); } log.info("Redis Cache id " + id); this.id = id; } @Override public String getId() { return this.id; } @Override public void putObject(Object key, Object value) { if (value != null) { // 向Redis中添加數據,有效時間是2天 redisTemplate.opsForValue().set(key.toString(), value, 2, TimeUnit.DAYS); } } @Override public Object getObject(Object key) { try { if (key != null) { Object obj = redisTemplate.opsForValue().get(key.toString()); return obj; } } catch (Exception e) { log.error(e.getMessage()); } return null; } @Override public Object removeObject(Object key) { try { if (key != null) { redisTemplate.delete(key.toString()); } } catch (Exception e) { log.error(e.getMessage()); } return null; } @Override public void clear() { log.info("清空緩存"); try { Set<String> keys = redisTemplate.keys("*:" + this.id + "*"); if (!CollectionUtils.isEmpty(keys)) { redisTemplate.delete(keys); } } catch (Exception e) { log.error(e.getMessage()); } } @Override public int getSize() { Long size = (Long) redisTemplate.execute(new RedisCallback<Long>() { @Override public Long doInRedis(RedisConnection connection) throws DataAccessException { return connection.dbSize(); } }); return size.intValue(); } @Override public ReadWriteLock getReadWriteLock() { return this.readWriteLock; } public static void setRedisConnectionFactory(RedisTemplate redisTemplate) { MybatisRedisCache.redisTemplate = redisTemplate; } }
在spring啟動的時候,將redisTemplate注入到MybatisRedisCache類中
1 package com.smartdata.pms.cache; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.data.redis.core.RedisTemplate; 5 import org.springframework.stereotype.Component; 6 7 /** 8 * @ProjectName: smartdata 9 * @Package: com.smartdata.pms.cache 10 * @ClassName: RedisCacheTransfer 11 * @Author: heluwei 12 * @Description: 13 * @Date: 2020/3/22 23:19 14 * @Version: 1.0 15 */ 16 @Component 17 public class RedisCacheTransfer { 18 19 @Autowired 20 public void setJedisConnectionFactory(RedisTemplate redisTemplate) { 21 MybatisRedisCache.setRedisConnectionFactory(redisTemplate); 22 } 23 }
在xml文件中加入<cache></cache>標簽
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 3 <mapper namespace="com.smartdata.pms.mapper.PmsProductMapper"> 4 <cache type="com.smartdata.pms.cache.MybatisRedisCache"> 5 <property name="eviction" value="LRU" /> 6 <property name="flushInterval" value="6000000" /> 7 <property name="size" value="1024" /> 8 <property name="readOnly" value="false" /> 9 </cache> 10 <select id="getPmsProductPage" resultType="com.smartdata.pms.entity.PmsProduct"> 11 select * from pms_product pp 12 where 1=1 13 and pp.del_flag = '0' 14 <if test="pmsProduct.proNo != null and pmsProduct.proNo != ''"> 15 and pp.pro_no = #{pmsProduct.proNo} 16 </if> 17 </select> 18 19 <select id="getOneById" parameterType="Long" resultType="String"> 20 select pro_no proNo from pms_product where 1=1 and pro_id = #{pId} 21 </select> 22 </mapper>
緩存的只有在xml中的。利用Mybatis-plus,查詢的接口沒有效果。
測試:
1 import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 2 import com.smartdata.pms.PmsApplication; 3 import com.smartdata.pms.mapper.PmsProductMapper; 4 import org.junit.Test; 5 import org.junit.runner.RunWith; 6 import org.springframework.beans.factory.annotation.Autowired; 7 import org.springframework.boot.test.context.SpringBootTest; 8 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 9 import org.springframework.transaction.annotation.Transactional; 10 11 12 /** 13 * @ProjectName: smartdata 14 * @Package: PACKAGE_NAME 15 * @ClassName: CacheTest 16 * @Author: heluwei 17 * @Description: 緩存測試 18 * @Date: 2020/3/21 18:34 19 * @Version: 1.0 20 */ 21 @RunWith(SpringJUnit4ClassRunner.class) 22 @SpringBootTest(classes = PmsApplication.class) 23 public class CacheTest{ 24 @Autowired 25 PmsProductMapper pmsProductMapper; 26 /** 27 * @Description: 一級緩存,因為使用了連接池,每次查詢自動commit。 28 */ 29 @Test 30 @Transactional(rollbackFor = Throwable.class) 31 public void testFistCache(){ 32 // 第一次查詢,緩存到一級緩存 33 System.out.println(pmsProductMapper.selectById(1)); 34 // 第二次查詢,直接讀取一級緩存 35 System.out.println(pmsProductMapper.selectById(1)); 36 } 37 38 /** 39 * 測試二級緩存效果 40 * 需要*Mapper.xml開啟二級緩存 41 **/ 42 @Test 43 public void testSecondCache(){ 44 System.out.println(pmsProductMapper.getOneById(1L)); 45 System.out.println(pmsProductMapper.getOneById(1L)); 46 System.out.println(pmsProductMapper.getOneById(1L)); 47 System.out.println(pmsProductMapper.getOneById(1L)); 48 System.out.println(pmsProductMapper.selectById(1L)); 49 System.out.println(pmsProductMapper.selectById(1L)); 50 System.out.println(pmsProductMapper.selectById(1L)); 51 System.out.println(pmsProductMapper.selectById(1L)); 52 } 53 }