Spring Cache and EhCache realize cache management

1. Know Spring Cache

Spring Cache is a set of caching solutions provided by Spring. It does not provide cache implementation itself, but provides a unified interface and code specifications, configuration, annotations, etc., in order to integrate various Cache solutions, so that users do not need to care about the details of Cache.

Spring supports "transparently" adding a cache to the application, applying the cache to the method, and checking whether there is available data in the cache before the method is executed. This can reduce the number of method executions and increase the speed of response. The application of the cache is "transparent" and will not cause any interference to the caller. As long as the caching support is enabled through the annotation @EnableCaching, Spring Boot will automatically handle the basic configuration of the cache.

Spring Cache acts on methods. When a cache method is called, the method parameters and the return result will be stored in the cache as a "key/value" (key / value), and the method will no longer be executed when the method is called with the same parameters next time. Instead, get the result directly from the cache and return it. Therefore, when using Spring Cache, you must ensure that the same results are returned when the cached method and method parameters are the same.

The declarative cache annotations provided by Spring Boot are as follows:

annotation Description
@EnableCaching Turn on caching.
@Cacheable It can be used on classes and methods to cache the return values ​​of classes or methods in the form of key-value pairs.
@CachePut The method is called and the result is cached.
@CacheEvict Empty the cache.
@Caching Used to combine multiple annotation tags.

Detailed use of declarative cache annotations: "Spring uses Cache and integrates Ehcache"

 

2. Get to know EhCache

Spring Boot supports many different caching products. Simple cache is used by default, and it is not recommended to use it in a formal environment. We can configure some more powerful caches, such as Ehcache.

Ehcache is a widely used open source Java distributed cache. It has features such as memory and disk storage, cache loader, cache extension, cache exception handling, GZIP cache, Servlet filter, and support for REST and SOAP API.

 

3. Create an integration project between SpringBoot and MyBatis

[Example] Create an integration project of SpringBoot and MyBatis to realize the functions of querying, adding, modifying, and deleting user information. And use Spring Cache and EhCache to achieve cache management, the execution results are as follows:

3.1 Create a data table

Create a user information table (tb_user) in the MySQL database and add data.

-- 判断数据表是否存在,存在则删除
DROP TABLE IF EXISTS tb_user;
 
-- 创建“用户信息”数据表
CREATE TABLE IF NOT EXISTS tb_user
( 
	user_id INT AUTO_INCREMENT PRIMARY KEY COMMENT '用户编号',
	user_name VARCHAR(50) NOT NULL COMMENT '用户姓名',
	age INT DEFAULT(0) NOT NULL COMMENT '年龄',
	blog_url VARCHAR(50) NOT NULL COMMENT '博客地址',
	blog_remark VARCHAR(50) COMMENT '博客信息'
) COMMENT = '用户信息表';
 
-- 添加数据
INSERT INTO tb_user(user_name,age,blog_url,blog_remark) VALUES('pan_junbiao的博客',32,'https://blog.csdn.net/pan_junbiao','您好,欢迎访问 pan_junbiao的博客');

3.2 Create a project 

(1) Create a SpringBoot project, the project structure is as follows:

(2) Add pom.xml configuration information

Add MyBatis, MySQL JDBC database driver, Spring Boot cache support starter, Ehcache cache, etc. in the pom.xml configuration file.

<!-- MyBatis与SpringBoot整合依赖 -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.3</version>
</dependency>

<!-- MySQL的JDBC数据库驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.20</version>
</dependency>

<!-- 引入Thymeleaf模板引擎 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<!-- Spring Boot缓存支持启动器 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
    <version>2.3.2.RELEASE</version>
</dependency>

<!-- Ehcache缓存管理器 -->
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
</dependency>

(3) Configuration related information

Modify the suffix of the default application.properties file to ".yml", that is, the configuration file name is: application.yml, and configure the following information:

#Spring配置
spring:
  #缓存管理器
  cache:
    type: ehcache
    ehcache:
      config: classpath:ehcache.xml #缓存加载配置文件
  #使用Thymeleaf模板引擎
  thymeleaf:
    mode: HTML5
    encoding: UTF-8
    cache: false  #使用Thymeleaf模板引擎,关闭缓存
    servlet:
      content-type: text/html
  #DataSource数据源
  datasource:
    url: jdbc:mysql://localhost:3306/db_admin?useSSL=false&amp
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

#MyBatis配置
mybatis:
  type-aliases-package: com.pjb.entity #别名定义
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #指定 MyBatis 所用日志的具体实现,未指定时将自动查找
    map-underscore-to-camel-case: true #开启自动驼峰命名规则(camel case)映射
    lazy-loading-enabled: true #开启延时加载开关
    aggressive-lazy-loading: false #将积极加载改为消极加载(即按需加载),默认值就是false
    #lazy-load-trigger-methods: "" #阻挡不相干的操作触发,实现懒加载
    cache-enabled: true #打开全局缓存开关(二级环境),默认值就是true

 

4. Configure EhCache cache manager

4.1 Create ehcache.xml configuration file

Under the resources (resource directory), create the ehcache.xml configuration file, the configuration information is as follows:

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

    <!-- 这个是磁盘存储路径,当内存缓存满了的时候,就会往这里面放,
      java.io.tmdir是操作系统缓存的临时目录,不同操作系统缓存目录不一样 -->
    <diskStore path="java.io.tmpdir"/>

    <!--defaultCache:echcache的默认缓存策略  -->
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxElementsOnDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </defaultCache>
    <cache name="userCache"
           maxElementsInMemory="10000"
           eternal="false"
           timeToIdleSeconds="120"
           timeToLiveSeconds="120"
           maxElementsOnDisk="10000000"
           diskExpiryThreadIntervalSeconds="120"
           memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </cache>
</ehcache>

Configuration attribute description: 

Attributes Description
<diskStore path="java.io.tmpdir"/> This is the disk storage path. When the memory cache is full, it will be placed in it. java.io.tmdir is the temporary directory of the operating system cache. The cache directory of different operating systems is different.
maxElementsInMemory The maximum number of elements that can be stored in the memory cache. If the elements placed in the Cache exceed this value, there are the following two situations:
(1) If overflowToDisk=true, the extra elements in the Cache will be placed in the disk file .
(2) If overflowToDisk=false, replace the original elements in the Cache according to the memoryStoreEvictionPolicy strategy.
overflowToDisk Whether to enable disk caching when the memory is insufficient.
eternal Whether the objects in the cache are permanently valid.
timeToIdleSeconds The allowable idle time (unit: second) of cached data before invalidation. It is only used when eternal=false. The default value is 0 which means the idle time is infinite. If no element in the cache is accessed for more than this time, then this element Will be cleared from the Cache.
timeToLiveSeconds The total survival time of the cached data (unit: second), only used when eternal=false, starting from creation, and ending with invalidation.
maxElementsOnDisk The maximum number of elements that can be stored in the disk cache, 0 means infinity.
diskExpiryThreadIntervalSeconds The running interval of the disk cache cleaning thread is 120 seconds by default.
memoryStoreEvictionPolicy Memory storage and release strategy, that is, when the maxElementsInMemory limit is reached, Ehcache will clean up the memory according to the specified strategy. There are three strategies: LRU (least recently used), LFU (most commonly used), and FIFO (first in first out).
defaultCache The default caching method.
cache Custom cache mode, set name by yourself.

4.2 Configure Cache Manager

Configure the target cache manager in the application.yml configuration file to support Ehcache, Generic, Redis, Jcache, etc. This configuration uses Ehcache.

#Spring配置
spring:
  #缓存管理器
  cache:
    type: ehcache
    ehcache:
      config: classpath:ehcache.xml #缓存加载配置文件

4.3 Turn on the cache function

Add the annotation @EnableCaching to the startup entry class of the SpringBoot project to enable the caching function.

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

@SpringBootApplication
@EnableCaching
public class SpringcacheDemoApplication
{
    public static void main(String[] args)
    {
        SpringApplication.run(SpringcacheDemoApplication.class, args);
    }
}

 

5. Use EhCache to implement cache management

5.1 Create an entity class (Entity layer)

In the com.pjb.entity package, create a UserInfo class (user information entity class).

package com.pjb.entity;

import java.io.Serializable;

/**
 * 用户信息实体类
 * @author pan_junbiao
 **/
public class UserInfo implements Serializable
{
    private int userId; //用户编号
    private String userName; //用户姓名
    private int age; //年龄
    private String blogUrl; //博客地址
    private String blogRemark; //博客信息

    //省略getter与setter方法...
}

Note: The entity class must implement the Serializable interface, otherwise the cache function cannot be implemented.

5.2 Database mapping layer (Mapper layer)

In the com.pjb.mapper package, create the UserMapper interface (user information Mapper dynamic proxy interface).

package com.pjb.mapper;

import com.pjb.entity.UserInfo;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;

/**
 * 用户信息Mapper动态代理接口
 * @author pan_junbiao
 **/
@Mapper
@Repository
public interface UserMapper
{
    /**
     * 根据用户ID,获取用户信息
     */
    @Select("SELECT * FROM tb_user WHERE user_id = #{userId}")
    public UserInfo getUserById(int userId);

    /**
     * 新增用户,并获取自增主键
     */
    @Insert("INSERT INTO tb_user(user_name,age,blog_url,blog_remark) VALUES(#{userName},#{age},#{blogUrl},#{blogRemark});")
    @Options(useGeneratedKeys = true, keyColumn = "user_id", keyProperty = "userId")
    public int insertUser(UserInfo userInfo);

    /**
     * 修改用户
     */
    @Update("UPDATE tb_user SET user_name = #{userName} ,age = #{age} ,blog_url = #{blogUrl} ,blog_remark = #{blogRemark} WHERE user_id = #{userId}")
    public int updateUser(UserInfo userInfo);

    /**
     * 删除用户
     */
    @Delete("DELETE FROM tb_user WHERE user_id = #{userId}")
    public int deleteUser(int userId);
}

5.3 Business logic layer (Service layer)

Create the UserService interface (user information business logic interface) under the com.pjb.service package.

package com.pjb.service;

import com.pjb.entity.UserInfo;

/**
 * 用户信息业务逻辑接口
 * @author pan_junbiao
 **/
public interface UserService
{
    /**
     * 根据用户ID,获取用户信息
     */
    public UserInfo getUserById(int userId);

    /**
     * 新增用户,并获取自增主键
     */
    public UserInfo insertUser(UserInfo userInfo);

    /**
     * 修改用户
     */
    public UserInfo updateUser(UserInfo userInfo);

    /**
     * 删除用户
     */
    public int deleteUser(int userId);
}

Under the com.pjb.service.impl package, create the UserServiceImpl class (user information business logic class).

package com.pjb.service.impl;

import com.pjb.entity.UserInfo;
import com.pjb.mapper.UserMapper;
import com.pjb.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
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;

/**
 * 用户信息业务逻辑类
 * @author pan_junbiao
 **/
//注意:必须对应配置文件ehcache.xml中cache节点的name属性值
//@CacheConfig(cacheNames = "userCache")
@Service
public class UserServiceImpl implements UserService
{
    @Autowired
    private UserMapper userMapper;

    //注意:必须对应配置文件ehcache.xml中cache节点的name属性值
    private static final String CACHE_NAME = "userCache";

    /**
     * 根据用户ID,获取用户信息
     */
    @Override
    @Cacheable(value = CACHE_NAME, key = "#userId")
    public UserInfo getUserById(int userId)
    {
        return userMapper.getUserById(userId);
    }

    /**
     * 新增用户,并获取自增主键
     */
    @Override
    @CachePut(value = CACHE_NAME, key = "#userInfo.userId")
    public UserInfo insertUser(UserInfo userInfo)
    {
        userMapper.insertUser(userInfo);
        return userInfo;
    }

    /**
     * 修改用户
     */
    @Override
    @CachePut(value = CACHE_NAME, key = "#userInfo.userId")
    public UserInfo updateUser(UserInfo userInfo)
    {
        userMapper.updateUser(userInfo);
        return userInfo;
    }

    /**
     * 删除用户
     */
    @Override
    @CacheEvict(value = CACHE_NAME, key = "#userId")
    public int deleteUser(int userId)
    {
        return userMapper.deleteUser(userId);
    }
}

As can be seen from the above code, the method of querying users uses the @Cacheable annotation to enable caching. Adding and modifying methods use the @CachePut annotation, which processes the method first, and then caches the result. To delete data, you need to use the @CacheEvict annotation to clear the cache.

@CacheConfig annotation: If all @Cacheable() has a value="xxx" attribute, it is obviously tiring to write if there are more methods. If you can declare it all at once, it will save trouble, so there is With @CacheConfig this configuration, @CacheConfig is a class-level annotation that allows to share the cache names. If you write another name in the method, then the method name will prevail.

5.4 Controller method (Controller layer)

In the com.pjb.controller package, create the UserController class (user controller) to realize the query, addition, modification, and deletion of user data, and realize the return of data.

package com.pjb.controller;

import com.pjb.entity.UserInfo;
import com.pjb.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

/**
 * 用户信息控制器
 * @author pan_junbiao
 **/
@Controller
@RequestMapping("/user")
public class UserController
{
    @Autowired
    private UserService userService;

    /**
     * 获取用户信息
     */
    @RequestMapping("getUserById")
    public ModelAndView getUserById(int userId)
    {
        //根据用户ID,获取用户信息
        UserInfo userInfo = userService.getUserById(userId);

        if(userInfo==null)
        {
            userInfo = new UserInfo();
        }

        //返回结果
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("userInfo",userInfo);
        modelAndView.setViewName("/user-info.html");
        return modelAndView;
    }

    /**
     * 新增用户
     */
    @ResponseBody
    @RequestMapping("insertUser")
    public boolean insertUser()
    {
        //创建新用户
        UserInfo userInfo = new UserInfo();
        userInfo.setUserName("pan_junbiao的博客");
        userInfo.setAge(32);
        userInfo.setBlogUrl("https://blog.csdn.net/pan_junbiao");
        userInfo.setBlogRemark("您好,欢迎访问 pan_junbiao的博客");

        //执行新增方法
        userService.insertUser(userInfo);

        //返回结果
        return userInfo.getUserId() > 0 ? true : false;
    }

    /**
     * 修改用户
     */
    @ResponseBody
    @RequestMapping("updateUser")
    public boolean updateUser(int userId)
    {
        UserInfo userInfo = new UserInfo();
        userInfo.setUserId(userId);
        userInfo.setUserName("pan_junbiao的博客_02");
        userInfo.setAge(35);
        userInfo.setBlogUrl("https://blog.csdn.net/pan_junbiao");
        userInfo.setBlogRemark("您好,欢迎访问 pan_junbiao的博客");

        //执行修改方法
        userService.updateUser(userInfo);

        //返回结果
        return true;
    }

    /**
     * 删除用户
     */
    @ResponseBody
    @RequestMapping("deleteUser")
    public boolean deleteUser(int userId)
    {
        //执行新增方法
        int result = userService.deleteUser(userId);

        //返回结果
        return result > 0 ? true : false;
    }
}

5.5 Display page (View layer)

Create a user-info.html user information display page under the resources/templates directory.

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>用户信息</title>
    <meta name="author" content="pan_junbiao的博客">
    <style>
        table { border-collapse: collapse; margin-bottom: 10px}
        table,table tr th, table tr td { border:1px solid #000000; padding: 5px 10px;}
    </style>
</head>
<body>

<div align="center">
    <table>
        <caption>用户信息</caption>
        <tr>
            <th>用户ID:</th>
            <td th:text="${userInfo.userId}"></td>
        </tr>
        <tr>
            <th>用户名称:</th>
            <td th:text="${userInfo.userName}"></td>
        </tr>
        <tr>
            <th>年龄:</th>
            <td th:text="${userInfo.age}"></td>
        </tr>
        <tr>
            <th>博客地址:</th>
            <td th:text="${userInfo.blogUrl}"></td>
        </tr>
        <tr>
            <th>备注信息:</th>
            <td th:text="${userInfo.blogRemark}"></td>
        </tr>
    </table>
</div>
</body>
</html>

So far, the project has been written and the execution result is as follows:

Then run other methods of the project, and then visit the URL of the query method multiple times to experience the cache effect. The main observation is whether the database has been operated. If the database has no operation data and returns data normally, it means that the cache is successful.

 

Guess you like

Origin blog.csdn.net/pan_junbiao/article/details/107999734