SpringBoot整合缓存1-Ehcache

SpringBoot整合缓存-Ehcache

一、缓存是什么?

缓存是一种将数据临时存储在高速存储介质(如内存)中的技术,目的是减少对数据库等低速存储的访问,提升系统响应速度。

Ehcache 是一个成熟的 Java 缓存框架,支持本地缓存分布式缓存(需配合 Terracotta 服务器),具备轻量、高效、可配置性强等特点。它是 Spring Cache 默认支持的缓存实现之一,通过 XML 或注解配置缓存策略(如过期时间、最大容量等),可直接与 Spring 生态整合。

二、选择 Ehcache 的优势

  1. 成熟稳定:作为老牌缓存框架,广泛应用于企业级项目,兼容性强。
  2. 灵活配置:支持通过 XML 精细定义缓存策略(如堆内 / 堆外存储、过期规则)。
  3. 本地缓存优先:无需额外部署服务(如 Redis),适合单体应用或对分布式缓存需求较低的场景。
  4. 与 Spring 无缝整合:通过 Spring Cache 注解即可快速使用,无需手动编写缓存逻辑。

三、案例实现过程

image-20251024180917975

以「图书管理系统」为例,实现图书信息的增删改查,并通过 Ehcache 缓存高频查询数据(如图书详情),减少数据库访问次数。

核心需求:查询图书时优先从缓存获取,新增 / 更新图书时同步更新缓存,删除图书时清除对应缓存。

1. 技术栈

  • SpringBoot 2.7.x:快速开发框架,简化配置
  • Ehcache 2.x:Java 本地缓存框架,支持自定义缓存策略(过期时间、容量等)
  • MyBatis-Plus 3.5.x:MyBatis 增强工具,简化 CRUD 操作
  • MySQL:关系型数据库(提供建表 SQL)

2.创建 Maven 项目Ehcache-Demo,配置 pom.xml

<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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.yqd</groupId>
    <artifactId>Ehcache-Demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>Ehcache-Demo</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- SpringBoot Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- 缓存抽象层 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

        <!-- Ehcache 2.x 缓存实现 -->
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
        </dependency>

        <!-- MyBatis-Plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3.1</version>
        </dependency>

        <!-- MySQL驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!-- Lombok(简化实体类) -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

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

</project>

3. 配置文件

(1)Ehcache 配置(src/main/resources/ehcache.xml)

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">

    <!-- 磁盘缓存位置(可选,本地缓存溢出时使用) -->
    <diskStore path="java.io.tmpdir/ehcache"/>

    <!-- 默认缓存配置(可选,未指定缓存策略时使用) -->
    <defaultCache maxEntriesLocalHeap="1000"
                  eternal="false"
                  timeToIdleSeconds="60"
                  timeToLiveSeconds="60"
                  overflowToDisk="false"/>

    <!-- 图书缓存策略(需与@CacheConfig的cacheNames对应) -->
    <cache name="bookCache"
           maxEntriesLocalHeap="200"
           eternal="false"
           timeToIdleSeconds="0"
           timeToLiveSeconds="60"
           overflowToDisk="false"/>
</ehcache>

(2)应用配置(src/main/resources/application.yml)

spring:
  # 数据库配置
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/book_cache_demo?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
    username: root  # 替换为你的MySQL用户名
    password: 123456  # 替换为你的MySQL密码

  # 缓存配置(指定Ehcache)
  cache:
    type: ehcache
    ehcache:
      config: classpath:ehcache.xml  # 加载自定义缓存策略

# MyBatis-Plus配置
mybatis-plus:
  mapper-locations: classpath:mapper/*.xml  # Mapper.xml路径
  type-aliases-package: com.yqd.entity  # 实体类包路径
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  # 打印SQL,验证缓存是否生效

(3)SQL脚本

-- 创建数据库
CREATE DATABASE IF NOT EXISTS book_cache_demo DEFAULT CHARACTER SET utf8;

-- 使用数据库
USE book_cache_demo;

-- 创建图书表
CREATE TABLE `t_book` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '图书ID',
  `book_name` varchar(100) NOT NULL COMMENT '图书名称',
  `author` varchar(50) NOT NULL COMMENT '作者',
  `publish_time` date DEFAULT NULL COMMENT '出版时间',
  `price` decimal(10,2) NOT NULL COMMENT '价格',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='图书表';

4. 代码实现

(1)实体类(com.yqd.entity.Book.java)
package com.yqd.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.math.BigDecimal;
import java.util.Date;

@Data
@TableName("t_book")  // 对应数据库表t_book(MyBatis-Plus已配置table-prefix: t_)
public class Book {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String bookName;  // 图书名称(对应数据库book_name)
    private String author;    // 作者
    private Date publishTime; // 出版时间
    private BigDecimal price; // 价格
}
(2)Mapper 接口(com.yqd.mapper.BookMapper.java)
package com.yqd.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yqd.entity.Book;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface BookMapper extends BaseMapper<Book> {
    // 无需手动编写CRUD方法,BaseMapper已提供(selectById、insert、updateById、deleteById等)
}
(3)Service 层(com.yqd.service.BookService.java)
package com.yqd.service;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yqd.entity.Book;
import com.yqd.mapper.BookMapper;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
@CacheConfig(cacheNames = "bookCache")  // 绑定ehcache.xml中的bookCache
public class BookService extends ServiceImpl<BookMapper, Book> {

    // 查询图书:缓存key=bookCache::id
    @Cacheable(key = "#id")
    public Book getById(Long id) {
        System.out.println("【数据库查询】id=" + id);  // 用于验证缓存是否生效
        return baseMapper.selectById(id);
    }

    // 新增图书:缓存key=bookCache::新增图书的id
    @CachePut(key = "#result.id")
    public Book add(Book book) {
        baseMapper.insert(book);
        return book;
    }

    // 更新图书:缓存key=bookCache::book.id
    @CachePut(key = "#book.id")
    public Book update(Book book) {
        baseMapper.updateById(book);
        return book;
    }

    // 删除图书:清除bookCache::id的缓存
    @CacheEvict(key = "#id")
    public void delete(Long id) {
        baseMapper.deleteById(id);
    }

    // 清除所有缓存
    @CacheEvict(allEntries = true)
    public void clearAll() {
        System.out.println("【清除所有缓存】");
    }
}
(4)Controller 层(com.yqd.controller.BookController.java)
package com.yqd.controller;

import com.yqd.entity.Book;
import com.yqd.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/books")
public class BookController {

    @Autowired
    private BookService bookService;

    // 查询图书
    @GetMapping("/{id}")
    public Book get(@PathVariable Long id) {
        return bookService.getById(id);
    }

    // 新增图书
    @PostMapping
    public Book add(@RequestBody Book book) {
        return bookService.add(book);
    }

    // 更新图书
    @PutMapping
    public Book update(@RequestBody Book book) {
        return bookService.update(book);
    }

    // 删除图书
    @DeleteMapping("/{id}")
    public String delete(@PathVariable Long id) {
        bookService.delete(id);
        return "删除成功";
    }

    // 清除所有缓存
    @DeleteMapping("/clear")
    public String clear() {
        bookService.clearAll();
        return "所有缓存已清除";
    }
}
(5)启动类(com.yqd.EhcacheDemoApplication.java)
package com.yqd;

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

@SpringBootApplication
@EnableCaching  // 开启Spring缓存功能
@MapperScan("com.yqd.mapper")  // 扫描Mapper接口
public class EhcacheDemo {
    public static void main(String[] args) {
        SpringApplication.run(EhcacheDemo.class, args);
    }
}

四、测试过程(使用 apipost或浏览器)

1. 新增图书
  • 请求POST http://localhost:8080/books

  • Body(JSON):

    {
      "bookName": "Java编程思想",
      "author": "Bruce Eckel",
      "publishTime": "2007-06-01",
      "price": 108.00
    }
    
  • 响应:返回新增的图书信息(包含自增 ID,例如id=1)。

2. 测试查询缓存
  • 首次查询GET http://localhost:8080/books/1
    • 控制台输出:【数据库查询】id=1(走数据库)。
    • 响应:返回图书信息。
  • 再次查询GET http://localhost:8080/books/1
    • 控制台无输出(走缓存)。
    • 响应:返回相同图书信息,速度更快。
3. 测试缓存更新
  • 更新图书PUT http://localhost:8080/books

    • Body(JSON):

      {
        "id": 1,
        "bookName": "Java编程思想(第4版)",
        "author": "Bruce Eckel",
        "publishTime": "2007-06-01",
        "price": 128.00
      }
      
  • 查询验证GET http://localhost:8080/books/1

    • 响应:返回更新后的价格(128.00),说明缓存已同步更新。
4. 测试缓存过期
  • 等待 60 秒(ehcache.xml 中配置的timeToLiveSeconds=60)。

  • 再次查询:

    GET http://localhost:8080/books/1
    
    • 控制台重新输出:【数据库查询】id=1(缓存过期,重新查库)。
5. 测试缓存删除
  • 删除图书DELETE http://localhost:8080/books/1

  • 查询验证:

    GET http://localhost:8080/books/1
    
    • 响应:null(数据库记录已删除,缓存也被清除)。
6. 测试清除所有缓存
  • 新增另一本图书(例如id=2),并查询确保缓存生效。
  • 清除缓存:DELETE http://localhost:8080/books/clear
  • 查询id=2的图书:控制台输出【数据库查询】id=2(缓存已被清除)。

五、预期结果

  • 首次查询走数据库,后续查询走缓存(无数据库日志)。
  • 更新操作后,缓存同步更新为新数据。
  • 缓存过期或删除后,查询重新走数据库。
  • 清除所有缓存后,所有查询均重新走数据库。
posted @ 2025-10-24 18:11  碧水云天4  阅读(6)  评论(0)    收藏  举报