欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring Boot---(10)SpringBoot中整合Ehcache缓存

程序员文章站 2022-03-15 14:56:55
...

SpringBoot中整合Ehcache缓存

 

为什么使用缓存?

问题:项目中查询的操作是最多的,很多查询都是重复的,但是频繁对数据库进行操作肯定会对数据库造成很大的压力,而且关系型数据库性能本身不是特别好,查询效率并不高。

解决方案:在项目当中做缓存机制,将重复的查询放到缓存中,直接从缓存中获取。

有很多种缓存的框架就不一一介绍的,该文章主要是了解如何在SpringBoot中使用Ehcache缓存框架。

浅谈Ehcache

Ehcahe是一个Java的进程内缓存框架,提供内存,磁盘文件存储,以及分布式存储方式等多种灵活的cache管理方案,Ehcache是Hibernate中默认使用的CaCheProvider,Hibernate就是使用Ehcache来使用二级缓存的。

在Spring中使用Ehcache时

主要是配置Ehcache缓存管理器(EhcacheManager),将其注入到Spring缓存管理器(CacheManager),Spring平台缓存管理器再注入到Spring缓存注解驱动。业务中只需要使用代码进行缓存。

整合Ehcache缓存中主要使用到的两个注解:

@Cacheable与@CacheEvict
      @Cacheable作用:将方法的返回值添加到Ehcache中做缓存。
          Value属性:指定ehcache配置文件中的缓存策略。如果没给定Value,则使用默认的缓存策略
          key属性:给缓存中的值起个名称,在查询时如果有名称相同的,则从已知缓存中将数据返回。
      @CacheEvict作用:清除缓存。

Ehcache默认的缓存中使用的策略介绍:

maxElementsInMemory:最大的缓存对象数量
eternal:是否持久化
timeToIdleSeconds:对象空闲时间,超过该时间没被访问则删除
timeToLiveSeconds:对象存活时间。
maxElementsOnDisk:磁盘上缓存的元素的最大数量
diskExpiryThreadIntervalSeconds:对象检测线程运行时间间隔。
memoryStoreEvictionPolicy:内存中数据超过内存限制,则向磁盘缓存。默认:LRU(最近最少使用),FIFO(先进先出),LFU(最少使用)

pom.xml的依赖项

<dependencies>
        <!-- springBoot的启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- thymeleaf的启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!-- 测试的jar包 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <!-- springboot整合jpa需要的依赖包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <!-- druid数据库连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.12</version>
        </dependency>
        <!-- mysql数据库驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.45</version>
        </dependency>
        <!-- SpringBoot缓存启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <!-- ehcache依赖包 -->
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
        </dependency>
    </dependencies>

application.yml配置文件

spring:
  #配置数据源
  datasource:
    driverClassName: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/ssm_crud?characterEncoding=utf-8
    username: root
    password: 123456
    #使用druid连接池
    type: com.alibaba.druid.pool.DruidDataSource
  jpa:
    hibernate:
      #正向工程,如果数据中没有表,则会创建一张表再更新数据,存在,则会直接更新数据
      ddl-auto: update
    #打印执行sql语句
    show-sql: true
  #配置ehcache缓存的配置文件
  cache:
    ehcache:
      config: classpath:ehcache.xml

ehcache.xml缓存机制的缓存策略

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">

    <diskStore path="java.io.tmpdir"/>

    <!-- defaultCache:ehcache的默认缓存策略 -->
    <!--maxElementsInMemory:最大的缓存对象数量
        eternal:是否持久化
        timeToIdleSeconds:对象空闲时间,超过该时间没被访问则删除
        timeToLiveSeconds:对象存活时间。
        maxElementsOnDisk:磁盘上缓存的元素的最大数量
        diskExpiryThreadIntervalSeconds:对象检测线程运行时间间隔。
        memoryStoreEvictionPolicy:内存中数据超过内存限制,则向磁盘缓存。默认:LRU(最近最少使用),FIFO(先进先出),LFU(最少使用)
     -->
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxElementsOnDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </defaultCache>

    <!-- 自定义缓存策略 -->
    <cache name="users"
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxElementsOnDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </cache>

</ehcache>

启动类

package com.kevin;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

/**
 * @author kevin
 * @version 1.0
 * @description     SpringBoot整合Ehcache缓存
 *
 *   @Cacheable与@CacheEvict
 *      @Cacheable作用:将方法的返回值添加到Ehcache中做缓存。
 *          Value属性:指定ehcache配置文件中的缓存策略。如果没给定Value,则使用默认的缓存策略
 *          key属性:给缓存中的值起个名称,在查询时如果有名称相同的,则从已知缓存中将数据返回。
 *      @CacheEvict作用:清除缓存。
 *
 *
 * @createDate 2019/3/21
 */
@SpringBootApplication
@EnableCaching      // 启动缓存
public class EhcacheApplication {

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

Users实体类

package com.kevin.entity;

import javax.persistence.*;
import java.io.Serializable;

/**
 * @author kevin
 * @version 1.0
 * @description     用户实体类
 *      想要使用缓存,需要将该对象实现Serializable接口做序列化
 * @createDate 2019/3/20
 */
@Entity     // 表示该类为实体类
@Table(name="t_users")      // 对应数据库中的表名
public class Users implements Serializable {

    @Id     // 表示为主键
    @GeneratedValue(strategy = GenerationType.IDENTITY)     // 该主键的策略
    @Column(name = "id")        // 列名
    private Integer id;
    @Column(name = "name")      // 列名
    private String name;
    @Column(name = "age")       // 列名
    private Integer age;
    @Column(name = "address")   // 列名
    private String address;

    public Users() {
    }

    public Users(String name, Integer age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    @Override
    public String toString() {
        return "Users{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

}

Dao层使用的是JpaRepository接口,无论使用什么持久层都使用一样的,这仅仅是一个示范

package com.kevin.dao;

import com.kevin.entity.Users;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * @author kevin
 * @version 1.0
 * @description         JpaRepository接口的使用,对继承的父接口中的方法的返回值进行适配。
 *      参数一 T :当前需要映射的实体类
 *      参数二 ID :当前映射的实体类中的OID的类型
 * @createDate 2019/3/20
 */
public interface UsersJpaRepository extends JpaRepository<Users,Integer> {

}

Service接口层

package com.kevin.service;

import com.kevin.entity.Users;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.util.List;

/**
 * @author kevin
 * @version 1.0
 * @description
 * @createDate 2019/3/21
 */
public interface UsersService {

    List<Users> findUserAll();

    Users findUserById(Integer id);

    Page<Users> findUserByPage(Pageable pageable);

    void saveUsers(Users users);

}

Service接口实现类,缓存的注解在该实现类中使用

package com.kevin.service.impl;

import com.kevin.dao.UsersJpaRepository;
import com.kevin.entity.Users;
import com.kevin.service.UsersService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * @author kevin
 * @version 1.0
 * @description     UsersService接口实现类
 *
 * @createDate 2019/3/21
 */
@Repository
public class UserServiceImpl implements UsersService {

    @Autowired
    private UsersJpaRepository usersJpaRepository;

    @Override
    @Cacheable(value = "users")
    public List<Users> findUserAll() {
        return this.usersJpaRepository.findAll();
    }

    @Override
    @Cacheable(value = "users" )  // 对当前查询的对象做缓存处理
    public Users findUserById(Integer id) {
        return this.usersJpaRepository.findById(id).get();
    }

    @Override
    // 缓存的key,可以为空,按照SpEL表达式编写,如果不指定,则将方法内所有的参数组合起来
    // 就算是查询同个方法,如果key中参数对象的值不一样,在缓存中找不到该key,则还是会从数据库中取出
    @Cacheable(value = "users", key = "#pageable")
    public Page<Users> findUserByPage(Pageable pageable) {
        return this.usersJpaRepository.findAll(pageable);
    }

    @Override
    @CacheEvict(value = "users", allEntries = true)        // 清除缓存中以users缓存策略缓存的对象
    public void saveUsers(Users users) {
        this.usersJpaRepository.save(users);
    }

}

测试Ehcache缓存,该测试使用SpringBoot中的Test

package com.kevin.test;

import com.kevin.EhcacheApplication;
import com.kevin.entity.Users;
import com.kevin.service.UsersService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * @author kevin
 * @version 1.0
 * @description     测试ehcache缓存
 * @createDate 2019/3/21
 */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = EhcacheApplication.class)
public class UserServiceTest {

    @Autowired
    private UsersService usersService;

    // ehcache缓存测试@Cacheable,默认key        在findUserById查询方法中加入缓存,第一次查询时查询了数据库,第二次直接从缓存取出
    @Test
    public void testFindUserById(){
        // 第一次查询
        Users user = this.usersService.findUserById(1);
        System.out.println(user);

        // 第二次查询
        Users user2 = this.usersService.findUserById(1);
        System.out.println(user2);

    }

    // ehcache缓存测试@Cacheable,自定义key
    @Test
    public void testFindUserByPage(){
        // 设置分页
        Pageable pageable = new PageRequest(0,2);
        // 第一次查询
        Page<Users> user = this.usersService.findUserByPage(pageable);
        System.out.println(user.getTotalElements());
        for (Users u:user) {
            System.out.println(u);
        }

        // 第二次查询
        Page<Users> user1 = this.usersService.findUserByPage(pageable);
        System.out.println(user.getTotalElements());
        for (Users u:user1) {
            System.out.println(u);
        }

        // 第三次查询
        pageable = new PageRequest(1, 2);
        Page<Users> user2 = this.usersService.findUserByPage(pageable);
        System.out.println(user.getTotalElements());
        for (Users u:user2) {
            System.out.println(u);
        }
    }


    // ehcache缓存测试@CacheEvict,清空缓存
    @Test
    public void testFindAll(){

        // 第一次查询,查询数据记录长度
        System.out.println(this.usersService.findUserAll().size());

        // 增加,长度加一
        Users users = new Users();
        users.setName("小波");
        users.setAge(24);
        users.setAddress("湛江");
        this.usersService.saveUsers(users);

        // 第二次查询,如果不清除缓存,则还是取到之前的数据,所以需要将缓存清除
        System.out.println(this.usersService.findUserAll().size());

    }


}

 

相关标签: 微服务