springboot-cache-mybatis

当程序中数据库访问压力特别大时,我们会考虑使用缓存来减少对数据库的访问次数。

在我们的项目中,springboot+Mybatis是一个比较常见的组合。Mybatis有一级缓存和二级缓存。

一级缓存:

1. 创建一个springboot项目

2. entity层和mapper层代码略

3. 在yml配置日志打印级别,打印出数据库查询语句

```
logging:
  level:
    top:
      kooper:
       demo:
        log:
          mapper: debug
```
其中top.kooper.demo.log.mapper包为数据库访问层

4. 编写测试代码

```
@Test
@Transactional
public void contextLoads() throws Exception {

    Log log = this.logService.get(1L);
    System.out.println(log.toString());

    Log log3 = this.logService.get(1L);
    System.out.println(log3.toString());
}
```
运行后日志打印:


```
2019-01-17 23:42:16.427 DEBUG 3689 --- [           main] t.k.d.l.m.LogMapper.selectByPrimaryKey   : ==>  Preparing: select id, `time`, content from log where id = ? 
2019-01-17 23:42:16.473 DEBUG 3689 --- [           main] t.k.d.l.m.LogMapper.selectByPrimaryKey   : ==> Parameters: 1(Long)
2019-01-17 23:42:16.554 DEBUG 3689 --- [           main] t.k.d.l.m.LogMapper.selectByPrimaryKey   : <==      Total: 1
Log(id=1, time=4, content=test)
Log(id=1, time=4, content=test)
```

从日志中我们可以看到程序只进行了一次数据库的查询,这是因为Mybatis默认是开启一级缓存。

一级缓存的作用域是一个SqlSession。在同一个SqlSession中,执行相同的查询SQL,第一次会去查询数据库,并写到缓存中;第二次直接从缓存中取。

注意

  • 当执行SQL时两次查询中间发生了增删改操作,则SqlSession的缓存清空。

  • 当Mybatis整合Spring后,直接通过Spring注入Mapper的形式,如果不是在同一个事务中每个Mapper的每次查询操作都对应一个全新的SqlSession实例,这个时候就不会有一级缓存的命中,但是在同一个事务中时共用的是同一个SqlSession。

二级缓存

由于一级缓存的局限性,更多的时候我们需要二级缓存,Mybatis默认是没有开启二级缓存。二级缓存的作用域是同一个namespace下的mapper映射文件内容,多个SqlSession共享。

一般使用cache+redis实现。

1. 引入pom

```
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-data-redis</artifactId>
   </dependency>
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-cache</artifactId>
   </dependency>
```

2. 配置文件redis

```
spring:
  redis:
    # Redis数据库索引(默认为0)
    database: 0
    # Redis服务器地址
    host: localhost
    # Redis服务器连接端口
    port: 6379
    ......
```

4. 在程序入口开启缓存

```
@SpringBootApplication
@EnableCaching
@EnableScheduling
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}
```

5. 在业务层接入缓存

```
@Service
@CacheConfig(cacheNames = {"log"})
public class LogService {

    @Autowired
    private LogMapper logMapper;

    @Cacheable(key = "#p0")
    public Log get(Long id) {
        Log log = this.logMapper.selectByPrimaryKey(id);
        return log;
    }

    @CachePut(key = "#p0.id")
    public Log update(Log log) {
        int update = this.logMapper.updateByPrimaryKey(log);
        return log;
    }

    @CacheEvict(key = "#p0")
    public void delete(Long id) {
        this.logMapper.deleteByPrimaryKey(id);
    }

}
```
注意

#p0是SpEL表达式,表示该方法的第一个参数

6. 其中注解的文档

@Cacheable

如果缓存存在,直接读取缓存值; 如果不存在调用目标方法,并将方法返回结果放入缓存
其中,注解中的属性值说明如下:

  • value: 缓存名,必填。
  • key:可选属性,可以使用SPEL标签自定义缓存的key。
  • condition:属性指定发生的条件。
@CachePut

使用该注解标识的方法,每次都会执行目标逻辑代码,并将结果存入指定的缓存中。之后另一个方法就可以直接从相应的缓存中取出缓存数据,而不需要再去查询数据库。

  • value:缓存名,必填。
  • key:可选属性,可以使用SPEL标签自定义缓存的key。
@CacheEvict

标记要清空缓存的方法,当这个方法被调用后,即会清空缓存。@CacheEvict注解属性说明如下:

  • value:必填
  • key:可选(默认是所有参数的组合)
  • condition:缓存的条件
  • allEntries:是否清空所有缓存内容,默认为 false,如果指定为 true,则方法调用后将立即清空所有缓存。
  • beforeInvocation:是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存。
@CacheConfig

主要用于配置该类中会用到的一些共用的缓存配置

7.缓存实现

我们可以通过配置属性spring.cache.type来强制指定缓存策略。

8. 清除缓存

  • 手动通过delete方法删除

  • 定时删除

       @CacheEvict(allEntries = true)
       @Scheduled(cron = "0/8 * * * * ?")
       public void schedulingEvict() {
       }
    

    虽然@Scheduled支持固定频率(fixRate)、固定延时(fixDelay)、cron表达式,但是带有@Scheduled注解的函数不能添加入参,所以在自动清除方面我们只能实现集合级别的清除,不能实现key级别的清除。

本文参考:
https://mrbird.cc/Spring-Boot cache.html
https://blog.csdn.net/qq_28929589/article/details/79127268

发布了20 篇原创文章 · 获赞 5 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_41762098/article/details/86533301
今日推荐