SpringBoot 2.0.4 使用Ehcache作为Hibernate的二级缓存和系统缓存

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/yingziisme/article/details/81436355

本文基于springboot 2.0.4 使用ehcache作为Hibernate的二级缓存 以及系统缓存
额外需要用的是mysql数据库

由于springboot2.x和1.x差别较大 使用1.x可能会有错误
另外ehcache版本也会导致有不同的问题 本文默认使用了spring-boot-starter-cache里面的ehcache

首先 先看一下POM文件的配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.demo.mt</groupId>
<artifactId>ehcache</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>ehcache</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.44</version>
</dependency>
<!-- 数据库动态条件查询 -->
<dependency>
<groupId>com.github.wenhao</groupId>
<artifactId>jpa-spec</artifactId>
<version>3.1.1</version>
</dependency>
<!-- 集成ehcache需要的依赖-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<exclusions>
<exclusion>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>


</project>

使用的时候遇到一个错误是

Caused by: net.sf.ehcache.CacheException: Another CacheManager with same name 'HIBERNATE_CACHE' already exists in the same VM. Please provide unique names for each CacheManager in the config or do one of following:
1. Use one of the CacheManager.create() static factory methods to reuse same CacheManager with same name or create one if necessary
2. Shutdown the earlier cacheManager before creating new one with same name.

这里是由于Ehcache新版本只能有一个CacheManager导致,新增一个ehcache配置文件提供给系统缓存
这里本来是新增了一个ehcache配置给hibernate 但是hibernate的配置项provider_configuration_file_resource_path似乎无效 可能是路径写的有问题 最终是修改了系统缓存文件路径解决了报错

这里是application.yml

spring:
  application:
    name: ehcache-demo
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/ehcachedb?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true
    username: root
    password: 123456
    type: com.zaxxer.hikari.HikariDataSource
    hikari:
      minimum-idle: 1
      maximum-pool-size: 100
      auto-commit: true
      idle-timeout: 30000
      pool-name: AdvZuulHikariCP
      max-lifetime: 1800000
      connection-timeout: 30000
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    properties:
      hibernate:
        format-sql: true
        cache:
          use_query_cache: true
          use_second_level_cache: true
          provider_configuration_file_resource_path: ehcache.xml
          region:
            factory_class: org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory
      javax:
        persistence:
          sharedCache:
            mode: ENABLE_SELECTIVE
  cache:
    type: ehcache
    ehcache:
      config: classpath:ehcache/ehcache-spring.xml
server:
  port: 10001

hibernate使用的ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>

<ehcache name="HIBERNATE_CACHE" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://www.ehcache.org/ehcache.xsd"
         updateCheck="true" monitoring="autodetect"
         dynamicConfig="true">

    <!--
      指定二级缓存存放在磁盘上的位置,可以使用磁盘目录,也可以使用Java System Property目录,user.home是用户目录、user.dir是用户当前工作目录、java.io.tmpdir是默认临时文件路径
      -->
    <diskStore path="../cache/hibernate"/>


    <transactionManagerLookup class="net.sf.ehcache.transaction.manager.DefaultTransactionManagerLookup"
                              properties="jndiName=java:/TransactionManager" propertySeparator=";"/>




    <defaultCache
            maxEntriesLocalHeap="0"
            eternal="false"
            timeToIdleSeconds="1200"
            timeToLiveSeconds="1200">
        <!--<terracotta/>-->
    </defaultCache>
    <!--可以给每个实体类指定一个配置文件,通过name属性指定,要使用类的全名
        1. name:Cache的唯一标识。
        2. maxElementsInMemory:内存中最大缓存对象数。
        3. eternal:Element是否永久有效,一旦设置true,timeout将不起作用。
        4. timeToIdleSeconds:设置Element在失效前的允许闲置时间。仅当element不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
        5. timeToLiveSeconds:设置Element在失效前允许存活时间。最大时间介于创建时间和失效时间之间。仅当element不是永久有效时使用,默认是0.,也就是element存活时间无穷大。
        6. overflowToDisk:配置此属性,当内存中Element数量达到maxElementsInMemory时,Ehcache将会Element写到磁盘中。
        7. maxElementsOnDisk:磁盘中最大缓存对象数,若是0表示无穷大。
        8. memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理缓存中的内容。默认策略是LRU(最近最少使用),你也可以设置为FIFO(先进先出)或是LFU(较少使用)

        9. diskSpoolBufferSizeMB  : 这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区
        10. maxEntriesLocalHeap堆内存中最大缓存对象数,0没有限制(必须设置)
        11. maxEntriesLocalDisk硬盘最大缓存个数

    -->
    <cache name="entityCache"
           maxEntriesLocalHeap="1000"
           maxEntriesLocalDisk="10000"
           eternal="false"
           diskSpoolBufferSizeMB="20"
           timeToIdleSeconds="10"
           timeToLiveSeconds="20"
           memoryStoreEvictionPolicy="LFU"
           transactionalMode="off">
        <persistence strategy="localTempSwap"/>

    </cache>

    <cache name="org.hibernate.cache.internal.StandardQueryCache"
           maxEntriesLocalHeap="5" eternal="false" timeToLiveSeconds="120">
        <persistence strategy="localTempSwap"/>

    </cache>

    <cache name="org.hibernate.cache.spi.UpdateTimestampsCache"
           maxEntriesLocalHeap="5000" eternal="true">
        <persistence strategy="localTempSwap"/>

    </cache>

</ehcache>

系统使用的ehcache-spring.xml

<?xml version="1.0" encoding="UTF-8"?>

<ehcache name="DEFAULT_CACHE" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://www.ehcache.org/ehcache.xsd"
         updateCheck="true" monitoring="autodetect"
         dynamicConfig="true">


    <diskStore path="../cache/default"/>

    <defaultCache
            maxEntriesLocalHeap="0"
            eternal="false"
            timeToIdleSeconds="1200"
            timeToLiveSeconds="1200">
        <!--<terracotta/>-->
    </defaultCache>


    <cache name="testCache"
           maxEntriesLocalHeap="1000"
           maxEntriesLocalDisk="10000"
           eternal="false"
           diskSpoolBufferSizeMB="20"
           timeToIdleSeconds="20"
           timeToLiveSeconds="20"
           memoryStoreEvictionPolicy="LFU"
           transactionalMode="off">
        <persistence strategy="localTempSwap"/>
        <cacheEventListenerFactory class="com.demo.mt.ehcache.config.ehcache.CustomerCacheEventListenerFactory"/>
    </cache>

</ehcache>

其中

<cacheEventListenerFactory class="com.demo.mt.ehcache.config.ehcache.CustomerCacheEventListenerFactory"/>

这一部分是可以不要的 自己实例化一个监听器可以方便做一些统计或者一些其他处理

首先是关于Hibernate的二级缓存

在主类上添加缓存注解@EnableCaching

@EnableCaching
@SpringBootApplication
public class EhcacheApplication {

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


}

在实体上加注解

@Data
@Entity
@Table(name = "role_tb")
@EqualsAndHashCode(callSuper = false)
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "entityCache")
public class RoleEntity extends BaseEntity<RoleEntity, Long> {

    protected String roleName;
}

这样就完成了hibernate的二级缓存配置
使用测试工具请求
GET http://localhost:10001/role/1
就可以测试缓存的效果了

前面用hibernate的二级缓存只能用于findById这类的请求 对于findAll则无效
不知道有没有其他什么配置

后来又使用了系统缓存
在controller层加了注解测试

package com.demo.mt.ehcache.controller;

import com.alibaba.fastjson.JSONObject;
import com.demo.mt.ehcache.entity.BaseEntity;
import com.demo.mt.ehcache.service.BaseService;
import com.demo.mt.ehcache.utils.ResultModel;
import com.github.wenhao.jpa.Specifications;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.MessageSource;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.io.Serializable;
import java.util.List;
import java.util.Optional;

/**
 * BaseController
 *
 * @author MT.LUO
 * 2018/7/9 11:34
 * @Description:
 */
@Slf4j
@CacheConfig(cacheNames = "testCache")
public class BaseController<S extends BaseService<T, ID>, T extends BaseEntity<T, ID>, ID extends Serializable> {
    @Autowired
    protected S baseManager;

    @Autowired
    private MessageSource messageSource;

    @Cacheable(key = "#root.target + '_' + #p0 + '_' + #p1")
    @RequestMapping(value = "/list", method = RequestMethod.GET)
    public ResultModel getList(@RequestParam int pageSize, @RequestParam int pageNumber) {
        log.info("BaseController list");
        Specification<T> specification = Specifications.<T>and().eq("deleted", false).build();

        if (pageSize > 0) {

            Pageable pageable = PageRequest.of(pageNumber, pageSize);
            Page<T> page = baseManager.findAll(specification, pageable);
            return ResultModel.ok(page);

        } else if (pageSize == -1) {
            return ResultModel.ok(baseManager.findAll());
        } else {
            List<T> entities = baseManager.findAll(specification);
            return ResultModel.ok(entities);
        }
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public ResultModel findOne(@PathVariable ID id) {
        return ResultModel.ok(baseManager.findOne(id));
    }
}

通过请求
GET http://localhost:10001/user/list?pageSize=10&pageNumber=0
可以测试缓存效果

这里贴出源码地址GITHUB

微信公众号不定时更新中
微信公众号:yingzi_code

展开阅读全文

没有更多推荐了,返回首页