Mybatis的一级缓存和二级缓存

一:一级缓存:

一级缓存在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 }

 

 

 

 

posted @ 2020-03-23 00:04  陆伟  阅读(1098)  评论(0编辑  收藏  举报