SpringBoot 整合 Encache 实现数据的缓存,本次使用技术是基于 SpringBoot 整合 Spring JPA 进行测试。通过配置 spring.jpa.show-sql=true
显性的观察到是否实现了数据的缓存,从而验证是否整合成功。
在 pom.xml 文件中添加 spring-boot-starter-cache
的坐标,使 SpringBoot 有支持 Encache 的基本Jar 环境。
org.springframework.boot
spring-boot-starter-cache
在 src/main/resources
中创建文件 ehcache.xml
。
cache
各个属性说明:
属性 | 属性说明 |
---|---|
name | 缓存名称 |
maxElementsInMemory | 缓存最大个数 |
eternal | 对象是否永久有效,一但设置了,timeout将不起作用 |
timeToIdleSeconds | 设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大 |
timeToLiveSeconds | 设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0,也就是对象存活时间无穷大 |
overflowToDisk | 当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中 |
diskSpoolBufferSizeMB | 这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区 |
maxElementsOnDisk | 硬盘最大缓存个数 |
diskPersistent | 是否缓存虚拟机重启期数,默认false |
diskExpiryThreadIntervalSeconds | 磁盘失效线程运行时间间隔,默认是120秒 |
memoryStoreEvictionPolicy | 当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。可设置为FIFO(先进先出)或LFU(较少使用) |
clearOnFlush | 内存数量最大时是否清除 |
application.properties
配置信息:
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mysql
spring.datasource.username=root
spring.datasource.password=123456
spring.jpa.database = MYSQL
spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto = none
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
spring.jpa.hibernate.naming.physical-strategy = org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
换成 yml
文件配置,如下:
spring:
### datasource Configuration
datasource:
url: jdbc:mysql://localhost:3306/mysql
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 123456
### hibernate Configuration
jpa:
database: mysql
show-sql: true
hibernate:
ddl-auto: none
naming:
implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
在启动类上添加注解 @EnableCaching
开启缓存。
package com;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
/**
* @Description 第一个SpringBoot 应用启动类
* @author 欧阳
* @since 2019年4月3日 下午6:14:39
* @version V1.0
*/
@SpringBootApplication
@EnableCaching
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
创建 Dao 文件 src/main/java/com/dao/UsersDao.java
文件名 UsersDao.java
package com.dao;
import org.springframework.data.jpa.repository.JpaRepository;
import com.bean.Users;
/**
* @Description 整合spring Jpa Dao
* @author 欧阳
* @since 2019年4月11日 下午6:14:26
* @version V1.0
*/
public interface UsersDao extends JpaRepository {
//需要使用其他的在这里定义方法
}
创建 Service 类 src/main/java/com/service/JpaService.java
package com.service;
import java.util.List;
import com.bean.Users;
/**
* @Description 整合spring Jpa Service
* @author 欧阳
* @since 2019年4月11日 下午2:17:37
* @version V1.0
*/
public interface JpaService {
/**
* 添加用户
* @param user
*/
public void insertUser(Users user);
/**
* 更新用户
* @param user
*/
public void updateUser(Users user);
/**
* 查询用户
* @param user
* @return
*/
public List selectUser();
/**
* 删除用户
* @param id
*/
public void deleteUser(String id);
}
实现该 Service src/main/java/com/service/impl/JpaServiceImpl.java
。
package com.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.bean.Users;
import com.dao.UsersDao;
import com.service.JpaService;
/**
* @Description 整合spring Jpa Service
* @author 欧阳
* @since 2019年4月11日 下午6:16:09
* @version V1.0
*/
@Service
public class JpaServiceImpl implements JpaService {
@Autowired
private UsersDao userDao;
@Override
@Transactional
//@CacheEvict(value="users",key="'user.selectUser'") 清除缓存中以 user.selectUser 为key 的缓存对象
@CacheEvict(value="users", key="'user.selectUser'")
public void insertUser(Users user) {
userDao.save(user);
}
@Override
@Transactional
public void updateUser(Users user) {
userDao.save(user);
}
@Override
@Cacheable(cacheNames="users",key="'user.selectUser'")
public List selectUser() {
return userDao.findAll();
}
@Override
@Transactional
public void deleteUser(String id) {
userDao.deleteById(id);
}
}
注意:这里用到了 @Cacheable
和 @CacheEvict
注解, @Cacheable
注解是把方法的返回值添加到 Ehcache 中做缓存,其中 Value 属性是指定一个 Ehcache 配置文件中的缓存策略,如果么有给定 value,name 则表示使用默认的缓存策略,Key 属性给存储的值起个名称。在查询时如果有名称相同的,那么则知己从缓存中将数据返回;而 @CacheEvict
注解是将 Ehcache 中的缓存清除掉。
同时,需要检查缓存的实体类是否实现了 Serializable
接口来序列化,因为对于对象的缓存会用到序列化和反序列化。
编写一个测试 Controller 来进行测试是否整合 Encache 成功。
package com.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.Cache.ValueWrapper;
import org.springframework.cache.CacheManager;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.bean.Users;
import com.service.JpaService;
/**
* @Description 整合 Ehcache
* @author 欧阳
* @since 2019年4月13日 上午12:37:52
* @version V1.0
*/
@RestController
public class EhcacheController {
@Autowired
private JpaService jpaService;
@Autowired
private CacheManager cacheManager;
@RequestMapping("/findAllUser")
public List findAllUser() {
//确保第一次查询是从数据库查询的数据,所以将缓存清除
//使用@CacheEvict 清除缓存
jpaService.insertUser(new Users("13", "李四"));
//第一次查询
System.out.println("---------------第一次查询----------------");
jpaService.selectUser();
//第二次查询
System.out.println("---------------第二次查询----------------");
jpaService.selectUser();
//第一次从缓存中获取查询的值
System.out.println("---------------第一次从缓存中取值----------------");
Cache cache = cacheManager.getCache("users");
ValueWrapper valueWrapper = cache.get("user.selectUser");
List result = null;
if(valueWrapper != null) {
result = (List)valueWrapper.get();
for(Users user : result) {
System.out.println(user);
}
}
//使用 CacheManager 清空缓存
cache.clear();
//第二次从缓存中获取查询的值
System.out.println("---------------第二次从缓存中取值----------------");
valueWrapper = cache.get("user.selectUser");
if(valueWrapper != null) {
result = (List)valueWrapper.get();
for(Users user : result) {
System.out.println(user);
}
}
//第三次查询
System.out.println("---------------第三次查询----------------");
jpaService.selectUser();
//第三次从缓存中获取查询的值
System.out.println("---------------第三次从缓存中取值----------------");
valueWrapper = cache.get("user.selectUser");
if(valueWrapper != null) {
result = (List)valueWrapper.get();
for(Users user : result) {
System.out.println(user);
}
}
return result;
}
}
运行程序启动类,清空控制台,接着我们访问 http://localhost:8080/findAllUser
,紧接着控制台打印如下信息:
2019-04-13 13:33:54.378 INFO 6464 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet'
2019-04-13 13:33:54.379 INFO 6464 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
2019-04-13 13:33:54.450 INFO 6464 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 71 ms
Hibernate: select users0_.id as id1_0_0_, users0_.name as name2_0_0_ from t_user users0_ where users0_.id=?
Hibernate: insert into t_user (name, id) values (?, ?)
---------------第一次查询----------------
Hibernate: select users0_.id as id1_0_, users0_.name as name2_0_ from t_user users0_
---------------第二次查询----------------
---------------第一次从缓存中取值----------------
Users(id=12, name=zhangsan)
Users(id=13, name=李四)
---------------第二次从缓存中取值----------------
---------------第三次查询----------------
Hibernate: select users0_.id as id1_0_, users0_.name as name2_0_ from t_user users0_
---------------第三次从缓存中取值----------------
Users(id=12, name=zhangsan)
Users(id=13, name=李四)
清空控制台,再次访问该 URL ,控制台打印:
Hibernate: select users0_.id as id1_0_0_, users0_.name as name2_0_0_ from t_user users0_ where users0_.id=?
---------------第一次查询----------------
Hibernate: select users0_.id as id1_0_, users0_.name as name2_0_ from t_user users0_
---------------第二次查询----------------
---------------第一次从缓存中取值----------------
Users(id=12, name=zhangsan)
Users(id=13, name=李四)
---------------第二次从缓存中取值----------------
---------------第三次查询----------------
Hibernate: select users0_.id as id1_0_, users0_.name as name2_0_ from t_user users0_
---------------第三次从缓存中取值----------------
Users(id=12, name=zhangsan)
Users(id=13, name=李四)
在配置有上述 application.properties
ehcache.xml
等配置文件的 SpringBoot 项目中,且配置有 spring.jpa.show-sql = true
的前提下,再综合 Dao 、Service 和 Controller,通过对控制台打印的信息进行分析,在第一次查询时 Users
的信息是从数据库中查询得到的,因为打印了 Hibernate: select users0_.id as id1_0_, users0_.name as name2_0_ from t_user users0_
;在第二次查询时没有打印 Hibernate sql 语句,紧接着第一次从缓存中取值是有值的,所以说明第二次查询是从缓存中取的,并且缓存中的Users 有两个值:Users(id=12, name=zhangsan) 和 Users(id=13, name=李四)
;回到 Controller 中,在第一次从缓存中取值和第二次从缓存中取值或在第三次查询前有清除缓存的代码 cache.clear();
,结合控制台打印的信息,说明代码 cache.clear();
生效,这个可通过第三次查询得知,因为第三次查询打印了 Hibernate sql 语句,这就更加证实了 cache.clear();
生效了;当第三次查询完之后又从缓存中取值,并且取到了和第一次从缓存中取的值一致。综上所述,在SpringBoot 中整合Encache实现了数据的缓存。
再此访问该 url ,打印的信息和第一次访问一样,特别是第一次查询时还是打印了 Hibernate sql 语句,并且在第一次访问时缓存中已经存在该查询的结果值,如果没有代码 jpaService.insertUser(new Users("13", "李四"));
时将再次从缓存中获取,这个可以从第二次查询时得到佐证,这说明代码 jpaService.insertUser(new Users("13", "李四"));
运行完后清除了缓存。因此使用 @CacheEvict
注解也可清空缓存。
CacheManager
实现缓存内容的查询和缓存清空;@Cacheable
和 @CacheEvict
实现缓存和缓存清空;cache.clear();
,方式二使用 @CacheEvict
注解在调用指定方法时清空缓存;