任务描述
任务要求
使用IDEA开发工具构建一个项目多模块工程。study-springboot-chapter08学习关于Springboot集成Ehcache缓存知识点
- 基于study-springboot工程,复制study-springboot-chapter00标准项目,坐标groupId(com.cbitedu)、artifactId(study-springboot-chapter08),其他默认
- 继承study-springboot工程依赖
- 详细学习Spring Mybatis。
- 引入缓存框架ehcache实现数据缓存
任务收获
- 如何集成第三方持久化技术Spring Mybatis
- 如何引入MySQL数据库依赖
- Spring Boot中整合ehcache
- 学会使用JUnit完成单元测试
- Ehcache缓存的使用
任务准备
环境要求
- JDK1.8+
- MySQL8.0.27+
- Maven 3.6.1+
- IDEA/VSCode
数据库准备
创建数据库platform,并创建用户表和初始化用户表数据。
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for tb_user
-- ----------------------------
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of tb_user
-- ----------------------------
INSERT INTO `tb_user` VALUES (1, 'tellsea', '654321');
INSERT INTO `tb_user` VALUES (2, 'susan', '123456');
INSERT INTO `tb_user` VALUES (3, 'super', '565656');
SET FOREIGN_KEY_CHECKS = 1;
工程目录要求
study-springboot-chapter08
任务实施
在Spring Boot中通过@EnableCaching注解自动化配置合适的缓存管理器(CacheManager),Spring Boot根据下面的顺序去侦测缓存提供者:
- Generic
- JCache (JSR-107) (EhCache 3, Hazelcast, Infinispan, and others)
- EhCache 2.x
- Hazelcast
- Infinispan
- Couchbase
- Redis
- Caffeine
- Simple
除了按顺序侦测外,我们也可以通过配置属性spring.cache.type来强制指定。我们也可以通过debug调试查看cacheManager对象的实例来判断当前使用了什么缓存。
当我们不指定具体其他第三方实现的时候,Spring Boot的Cache模块会使用ConcurrentHashMap来存储。而实际生产使用的时候,因为我们可能需要更多其他特性,往往就会采用其他缓存框架,所以接下来我们会分几篇分别介绍几个常用优秀缓存的整合与使用。
使用EhCache
介绍如何在Spring Boot中使用EhCache进程内缓存,测试验证用例(涵盖了CacheManager的注入,可用来观察使用的缓存管理类)
接下来我们通过下面的几步操作,就可以轻松的把上面的缓存应用改成使用ehcache缓存管理。
第一步:在pom.xml中引入ehcache依赖
<!--开启 cache 缓存-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- ehcache 缓存 -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- mybatis -->
<!-- 实现对 MyBatis 的自动化配置 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<!-- 通用Mapper启动器 -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
在Spring Boot的parent管理下,不需要指定具体版本,会自动采用Spring Boot中指定的版本号。
第二步:在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">
<defaultCache
eternal="false"
maxElementsInMemory="1000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="0"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"/>
<!-- 这里的 users 缓存空间是为了下面的 demo 做准备 -->
<cache
name="users"
eternal="false"
maxElementsInMemory="100"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="0"
timeToLiveSeconds="300"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
<!--
diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。
defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
name:缓存名称。
maxElementsInMemory:缓存最大数目
maxElementsOnDisk:硬盘最大缓存个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
overflowToDisk:是否保存到磁盘,当系统当机时
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
FIFO,first in first out,先进先出。
LFU, Less Frequently Used,一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
-->
第三步:完成任务准备中的数据库工作,创建表
第四步:分别在com.cbitedu.springboot下创建软件包
- 实体类:entity
- 接口层:mapper
- 服务层:service
- 控制台:controller
创建tb_user表的映射对象User
( com.cbitedu.springboot.entity )
package com.cbitedu.springboot.entity;
import lombok.Data;
import tk.mybatis.mapper.annotation.KeySql;
import javax.persistence.Id;
import javax.persistence.Table;
@Data
@Table(name = "tb_user")
public class User {
@Id
@KeySql(useGeneratedKeys = true)
private Long id;
private String name;
private String password;
}
创建映射接口mapper( com.cbitedu.springboot.dao )
package com.cbitedu.springboot.dao;
import com.cbitedu.springboot.entity.User;
import tk.mybatis.mapper.common.Mapper;
public interface UserMapper extends Mapper<User> {
}
创建服务接口UserService ( com.cbitedu.springboot.service )
package com.cbitedu.springboot.service;
import com.cbitedu.springboot.entity.User;
import java.util.List;
public interface UserService {
void insert(User user);
void update(User user);
void delete(Long id);
User get(Long id);
List<User> list();
}
创建服务接口实现类UserServiceImpl ( com.cbitedu.springboot.service.impl )
package com.cbitedu.springboot.service.impl;
import com.cbitedu.springboot.dao.UserMapper;
import com.cbitedu.springboot.entity.User;
import com.cbitedu.springboot.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
import org.springframework.util.CollectionUtils;
import java.util.List;
@Service
@CacheConfig(cacheNames = "users")//缓存名字
public class UserServiceImpl implements UserService {
private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
@Autowired
private UserMapper userMapper;
//这里的单引号不能少,否则会报错,被识别是一个对象
private static final String CACHE_KEY = "'user'";
//新增
@Override
@CachePut(key = CACHE_KEY)//先执行方法,然后将返回值存入缓存
public void insert(User user) {
int count = userMapper.insert(user);
if (count != 1) {
logger.info("新增失败");
}
}
//更新
@Override
@CachePut(key = "'user_'+#user.getId()")//先执行方法,然后将返回值存入缓存
public void update(User user) {
int count = userMapper.updateByPrimaryKey(user);
if (count != 1) {
logger.info("更新失败");
}
}
//删除
@Override
@CacheEvict(key = "'user_'+#id") //清除缓存
public void delete(Long id) {
int count = userMapper.deleteByPrimaryKey(id);
if (count != 1) {
logger.info("删除失败");
}
}
//查找
@Override
@Cacheable(key = "'user_'+#id")//先查缓存,有就直接返回,没得就把返回值加入缓存
public User get(Long id) {
return userMapper.selectByPrimaryKey(id);
}
@Override
@Cacheable(key = "'users'")
public List<User> list() {
List<User> list = userMapper.selectAll();
if (!CollectionUtils.isEmpty(list)) {
return list;
}
return null;
}
/*
* @Cacheable : Spring在每次执行前都会检查Cache中是否存在相同key的缓存元素,如果存在就不再执行该方法,
* 而是直接从缓存中获取结果进行返回,否则才会执行并将返回结果存入指定的缓存中。
* @CacheEvict : 清除缓存。
* @CachePut : 也可以声明一个方法支持缓存功能。使用@CachePut标注的方法在执行前不会去检查缓存
* 中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。
* 这三个方法中都有两个主要的属性:value 指的是 ehcache.xml 中的缓存策略空间;key 指的是缓存的标识,同时可以用 # 来引用参数。
* */
}
创建EhCacheConfig配置类EhCacheConfig,(com.cbitedu.springboot.config)
package com.cbitedu.springboot.config;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Configuration;
/**
* 启用EhCache缓存
*/
@Configuration
@EnableCaching
public class EhCacheConfig {
}
新建测试类SpringCacheTest
package com.cbitedu.springboot.web;
import com.cbitedu.springboot.entity.User;
import com.cbitedu.springboot.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringCacheTest {
@Test
public void contextLoads() {
}
private static final Logger logger = LoggerFactory.getLogger(SpringCacheTest.class);
@Autowired
private UserService userService;
/**
* 新增
*/
@Test
public void insert() {
User user = new User();
user.setName("insert");
user.setPassword("123456");
userService.insert(user);
}
/**
* 更新
*/
@Test
public void update() {
User user = new User();
user.setId(1L);
user.setName("tellsea");
user.setPassword("654321");
userService.update(user);
}
/**
* 删除
*/
@Test
public void delete() {
userService.delete(2L);
}
/**
* 查询单个
*/
@Test
public void get() {
User user = userService.get(1L);
User user1 = userService.get(1L);
logger.info("第一次:" + user);
logger.info("第二次:" + user1);
}
/**
* 查询所有
*/
@Test
public void list() {
List<User> list = userService.list();
list.forEach(user ->logger.info(user.toString()));
logger.info("第二遍");
List<User> list2 = userService.list();
list2.forEach(user -> logger.info(user.toString()));
}
}
- 第一行输出的打印了执行的SQL
- 第二次查询的时候,没有输出SQL语句,所以是走的缓存获取
实验实训
1、Ehcache缓存,如何清除缓存(插入、修改和删除数据)?
详细教程:www.cnblogs.com/hanease/p/1…
基本介绍: Ehcache 是一种基于标准的开源缓存,可提高性能,卸载数据库并简化可伸缩性。
它是使用最广泛的基于 Java 的缓存,因为它功能强大,经过验证,功能齐全,并与其他流行的库和框架集成。
Ehcache 可以从进程内缓存扩展到使用 TB 级缓存的混合进程内/进程外部署。