springboot 配置mybatis的二级缓存机制,使用redis配置二级缓存demo代码

springboot 配置mybatis的二级缓存机制

一级缓存 是sqlSession级别的缓存,面向单个访问用户
二级缓存 是namespace级别的缓存,同一个namespace内查询会缓存,一旦发生增删改操作则会清空该namespace缓存,二级缓存面向所有访问用户;二级缓存默认使用hashMap缓存,支持自定义缓存:mybatis支持encache和redis

mybatis 开启二级缓存的两种方式

首先配置 开启MyBatis的全局二级缓存
.properties文件或.yml 文件 增加配置
#推荐
mybatis.configuration.cache-enabled=true

或者

mybatis-config.xml文件增加配置
<settings>
      <!--显示的开启全局缓存-->
      <setting name="cacheEnabled" value="true"/>
</settings>
每个namespace只能选择其中一种方式
1.注解开启

//RedisCache.class为自定义配置类,实现org.apache.ibatis.cache.Cache接口
@CacheNamespace(implementation = RedisCache.class,eviction = RedisCache.class)
注解方式适用于持久层使用@Select、@insert等注解配置SQL语句
如果使用默认hashMap缓存,则@CacheNamespace

2.mapper文件开启

持久层mapper文件增加标签开启本mapper namespace下的二级缓存

<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
属性说明
 a. eviction:清除策略为FIFO缓存,先进先出原则,默认的清除策略是 LRU
 b. flushInterval:属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量
 c. size:最多可以存储结果对象或列表的引用数
 d. readOnly:只读属性,可以被设置为 truefalse

mybatis开启redis二级缓存 demo代码

1.redis缓存配置类:实现org.apache.ibatis.cache.Cache接口
/**
 * @program: test
 * @description: mybatis 使用redis实现二级缓存
 * @author: 闲走天涯
 * @create: 2021-08-24 16:32
 */
public class RedisCache implements Cache {
    //读写锁
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);

    private RedisTemplate<String,Object> redisTemplate;

    //缓存实例id 名称
    private String id;

    public RedisCache(String id) {
        if(id==null){
            throw new IllegalArgumentException("为获取到缓存实例id");
        }
        this.id = id;
    }

    //返回cache的唯一名称
    public String getId() {
        return id;
    }

    /**
     * 获取redisTemplate对象
     * @return
     */
    public RedisTemplate<String, Object> getRedisTemplate() {
        return ApplicationContextHolder.getBean("redisTemplate");
    }

    /**
     * 缓存存值
     * @param key
     * @param value
     */
    @Override
    public void putObject(Object key, Object value) {
        getRedisTemplate().opsForHash().put(id,key.toString(),value);
    }

    /**
     * 缓存取值
     * @param key
     * @return
     */
    @Override
    public Object getObject(Object key) {
        return getRedisTemplate().opsForHash().get(id,key.toString());
    }

    /**
     * 保留方法
     * @param key
     * @return
     */
    @Override
    public Object removeObject(Object key) {
        return null;
    }

    /**
     * 清空缓存,在增删改时会自动调动
     */
    @Override
    public void clear() {
        getRedisTemplate().delete(id);
    }

    /**
     * 获取缓存数量
     * @return
     */
    @Override
    public int getSize() {
        return getRedisTemplate().opsForHash().size(id).intValue();
    }

    /**
     * 获取读写锁对象
     * @return
     */
    @Override
    public ReadWriteLock getReadWriteLock() {
        return this.readWriteLock;
    }
}
2.配置获取容器中redisTemplate对象:实现ApplicationContextAware 类
/**
 * @program: test
 * @description: 容器类
 * @author: 闲走天涯
 * @create: 2021-08-24 17:01
 */
@Component
public class ApplicationContextHolder implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ApplicationContextHolder.applicationContext = applicationContext;
    }

    public static ApplicationContext getApplicationContext() {
        checkApplicationContext();
        return applicationContext;
    }

    /**
     * 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
     * @param name
     * @param <T>
     * @return
     */
    public static <T> T getBean(String name) {
        checkApplicationContext();
        return (T) applicationContext.getBean(name);
    }

    /**
     * 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(Class<T> clazz){
        checkApplicationContext();
        return applicationContext.getBean(clazz);
    }

    /**
     * 清除applicationContext静态变量.
     */
    public static void cleanApplicationContext() {
        applicationContext = null;
    }
    /**
     * 检查是否容器中是否存在
     */
    private static void checkApplicationContext() {
        if (applicationContext == null) {
            throw new IllegalStateException("applicaitonContext未注入,请在applicationContext.xml中定义SpringContextHolder");
        }
    }
}
3.学生信息vo类
/**
 * 描述:
 * 学生信息 vo类,需要实现序列化接口
 * @author 闲走天涯
 * @create 2021/7/19 15:33
 */
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
@ApiModel(value = "MerchantVo", description = "商户信息")
public class Student implements Serializable {
    @ApiModelProperty(value = "学生id,对应student表 s_id")
    private Long sId;
    @ApiModelProperty(value = "学生姓名,对应student表 s_name")
    private String sName;
    @ApiModelProperty(value = "学生生日,对应student表 s_birth")
    private String sBirth;
    @ApiModelProperty(value = "学生性别,对应student表 s_sex")
    private String sSex;
}
4.持久层 接口 注解方式
/**
 * @program: test
 * @description: 二级缓存dao
 * @author: 闲走天涯
 * @create: 2021-08-24 15:11
 *
 * @CacheNamespace 开启二级缓存 (根据命名空间namespace,例如com.cache.dao.CacheDao)
 * implementation 属性:默认是PerpetualCache类,即hashMap实现
 * 如果该命名空间namespace发生增删改操作,则会将该空间的缓存全部清空
 */
@CacheNamespace(implementation = RedisCache.class,eviction = RedisCache.class)
@Repository
public interface CacheDao {
    @Select({
            "<script>",
            "select * from student s",
            "<where>",
            "<if test='sId!=null and sId!=&apos;&apos;'>",
            "AND s.s_id = #{sId}",
            "</if>",
            "<if test='sName!=null and sName!=&apos;&apos;'>",
            "AND s.s_name = #{sName}",
            "</if>",
            "</where>",
            "</script>",
    })
    Page<Student> getStudentByPage(Student student);

    @Select("select * from student s where s.s_id=#{sid}")
    Student getStudentById(@Param("sid")Long sid);

    @Update({
            "<script>",
            "update student",
            "<set>",
            "<if test='sName!=null and sName!=&apos;&apos;'>",
            "s_name=#{sName},",
            "</if>",
            "<if test='sBirth!=null and sBirth!=&apos;&apos;'>",
            "s_birth=#{sBirth},",
            "</if>",
            "<if test='sSex!=null and sSex!=&apos;&apos;'>",
            "s_sex=#{sSex},",
            "</if>",
            "</set>",
            "<where>",
            "s_id = #{sId}",
            "</where>",
            "</script>",
    })
    int updateStudent(Student student);
}
5.service接口 和 serviceImpl 业务层实现类
/**
 * @program: test
 * @description: mybatis 二级缓存service
 * @author: 闲走天涯
 * @create: 2021-08-24 15:12
 */
public interface CacheService {
    PageInfo<Student> getStudentByPage(Student student,Integer pageNum,Integer pageSize);

    Student getStudentById(Long sid);

    int updateStudent(Student student);
}

/**
 * @program: test
 * @description: 二级缓存serviceimpl
 * @author: 闲走天涯
 * @create: 2021-08-24 15:14
 */
@Service
public class CacheServiceImpl implements CacheService {

    @Autowired
    private CacheDao cacheDao;

    /**
     * 分页查询学生
     * @param student
     * @return
     */
    @Override
    public PageInfo<Student> getStudentByPage(Student student,Integer pageNum,Integer pageSize) {
        if (pageNum == null || pageNum <= 0) pageNum = 1;
        if (pageSize == null || pageSize <= 0) pageSize = 10;
        PageHelper.startPage(pageNum, pageSize);
        Page<Student> page = cacheDao.getStudentByPage(student);
        return new PageInfo<>(page);
    }

    /**
     * 根据id获取学生信息
     * @param sid
     * @return
     */
    @Override
    public Student getStudentById(Long sid){
        return cacheDao.getStudentById(sid);
    }

    /**
     * 根据id修改学生信息
     * @param student
     * @return
     */
    @Override
    public int updateStudent(Student student){
        int i = cacheDao.updateStudent(student);
        if(i==1){
            return i;
        }else{
            throw new RuntimeException("修改异常");
        }
    }
}
6.控制层controller
/**
 * @program: test
 * @description: mybatis 开启二级缓存 (自定义缓存:mybatis支持encache和redis)
 *             一级缓存以sqlSession为主,面向单个访问用户
 *            二级缓存以namespace为标识,同一个namespace内查询会缓存,一旦发生增删改操作则会清空该namespace缓存,二级缓存面向所有访问用户
 * @author: 闲走天涯
 * @create: 2021-08-24 15:10
 */
@RestController
@RequestMapping(value = "/cache")
public class CacheController {
    @Autowired
    private CacheService cacheService;

    /**
     * 分页查询学生信息
     * @param student
     * @param pageNum
     * @param pageSize
     * @return
     */
    @RequestMapping("/getStudentByPage")
    public JsonBean getStudentByPage(Student student,Integer pageNum,Integer pageSize){
        try {
            PageInfo<Student> pageInfo = cacheService.getStudentByPage(student,pageNum,pageSize);
            return new JsonBean(true, "查询成功", 1, pageInfo);
        }catch (Exception e){
            e.printStackTrace();
        }
        return new JsonBean(false,"查询异常",0,null);
    }

    /**
     * 根据学生id查询学生信息
     * @param sid
     * @return
     */
    @RequestMapping(value = "/getStudentById")
    public JsonBean getStudentById(Long sid){
        try{
            if(!VerifyData.longIsNotNull(sid)){
                return new JsonBean(false,"参数不能为空",0,null);
            }
            Student student = cacheService.getStudentById(sid);
            return new JsonBean(true,"查询成功",1,student);
        }catch (Exception e){
            e.printStackTrace();
        }
        return new JsonBean(false,"查询异常",0,null);
    }

    /**
     * 更细学生信息
     * @param student
     * @return
     */
    @RequestMapping(value = "/updateStudent")
    public JsonBean updateStudent(Student student){
        try{
            if(!VerifyData.longIsNotNull(student.getSId())){
                return new JsonBean(false,"参数不能为空",0,null);
            }
            int i = cacheService.updateStudent(student);
            return new JsonBean(true,"修改成功",1,i);
        }catch (Exception e){
            e.printStackTrace();
        }
        return new JsonBean(false,"修改异常",0,null);
    }
}

7.测试

http://127.0.0.1:1234/cache/getStudentById?sid=5

http://127.0.0.1:1234/cache/getStudentByPage

查询redis hvals com.cache.dao.CacheDao:存在缓存数据,多次重复访问,不会再次访问数据库,从redis中获取
在这里插入图片描述

http://127.0.0.1:1234/cache/updateStudent?sId=7&sName=郑竹2

更新操作后缓存被清除
在这里插入图片描述
重新访问

http://127.0.0.1:1234/cache/getStudentById?sid=5

需要重新访问数据库

1. 引入依赖 在 pom.xml 文件中添加以下依赖: ``` <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-extension</artifactId> <version>3.4.2</version> </dependency> <dependency> <groupId>org.apache.ibatis</groupId> <artifactId>mybatis-redis-cache</artifactId> <version>2.0.1</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency> ``` 2. 配置 Redis 在 application.yml 配置文件中添加 Redis 的连接信息: ``` spring: redis: host: 127.0.0.1 port: 6379 password: 123456 database: 0 pool: max-active: 8 max-idle: 8 min-idle: 0 max-wait: -1ms ``` 3. 配置 MyBatis Plus 在 MyBatis Plus 配置文件中开启二级缓存配置 Redis 缓存: ``` mybatis-plus: configuration: cache-enabled: true # 开启二级缓存 local-cache-scope: session # 二级缓存作用域 lazy-loading-enabled: true # 开启懒加载 multiple-datasource-enabled: true # 开启多数据源 global-config: db-config: id-type: auto # 主键ID型 field-strategy: not_empty # 字段非空验证 swagger2: true # 是否开启Swagger2 cache: enabled: true # 开启缓存 type: redis # 缓存型 # 设置缓存前缀,默认是 mybatis:cache: # prefix: mybatisplus: spring: redis: cache: # 过期时间,默认1天 ttl: 86400 # 二级缓存前缀,默认是 mybatisplus:cache: key-prefix: mybatis-plus:cache: # 条目数量,默认256个 max-number-of-elements-in-cache: 256 ``` 4. 实体开启缓存 在需要开启缓存的实体上添加 `@CacheNamespace` 注解: ``` @Data @NoArgsConstructor @TableName("student") @CacheNamespace(implementation = MybatisRedisCache.class, eviction = MybatisRedisCache.class, flushInterval = 60000) public class Student implements Serializable { @TableId(type = IdType.AUTO) private Long id; @TableField("name") private String name; @TableField("age") private Integer age; } ``` 其中,`implementation` 和 `eviction` 属性的值均为 `MybatisRedisCache.class`,表示该实体使用 Redis 缓存,并且缓存失效时间为 1 分钟(60 秒)。 5. 注册 Redis 缓存插件 在 Spring Boot 应用启动中注册 Redis 缓存插件: ``` @Configuration public class MyBatisPlusConfig { @Bean public RedisCachePlugin redisCachePlugin(RedisTemplate<Object, Object> redisTemplate) { return new RedisCachePlugin(redisTemplate); } } ``` 6. 测试缓存 使用以下代码进行测试: ``` @Test public void testRedisCache() { Student student1 = studentService.getById(1L); Student student2 = studentService.getById(1L); System.out.println("student1:" + student1); System.out.println("student2:" + student2); Assert.assertEquals(student1, student2); } ``` 第一次查询会从数据库中获取数据并保存到 Redis 缓存,第二次查询会直接从 Redis 缓存中获取数据,输出结果如下: ``` DEBUG [MybatisRedisCache] [Session XX] [Namespace com.example.demo.entity.Student] [Cache INSERT] Student(id=1, name=Tom, age=20) student1:Student(id=1, name=Tom, age=20) student2:Student(id=1, name=Tom, age=20) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值