Spring Boot---(10)SpringBoot中整合Ehcache缓存
SpringBoot中整合Ehcache缓存
为什么使用缓存?
问题:项目中查询的操作是最多的,很多查询都是重复的,但是频繁对数据库进行操作肯定会对数据库造成很大的压力,而且关系型数据库性能本身不是特别好,查询效率并不高。
解决方案:在项目当中做缓存机制,将重复的查询放到缓存中,直接从缓存中获取。
有很多种缓存的框架就不一一介绍的,该文章主要是了解如何在SpringBoot中使用Ehcache缓存框架。
浅谈Ehcache
Ehcahe是一个Java的进程内缓存框架,提供内存,磁盘文件存储,以及分布式存储方式等多种灵活的cache管理方案,Ehcache是Hibernate中默认使用的CaCheProvider,Hibernate就是使用Ehcache来使用二级缓存的。
在Spring中使用Ehcache时
主要是配置Ehcache缓存管理器(EhcacheManager),将其注入到Spring缓存管理器(CacheManager),Spring平台缓存管理器再注入到Spring缓存注解驱动。业务中只需要使用代码进行缓存。
整合Ehcache缓存中主要使用到的两个注解:
@Cacheable与@CacheEvict
@Cacheable作用:将方法的返回值添加到Ehcache中做缓存。
Value属性:指定ehcache配置文件中的缓存策略。如果没给定Value,则使用默认的缓存策略
key属性:给缓存中的值起个名称,在查询时如果有名称相同的,则从已知缓存中将数据返回。
@CacheEvict作用:清除缓存。
Ehcache默认的缓存中使用的策略介绍:
maxElementsInMemory:最大的缓存对象数量
eternal:是否持久化
timeToIdleSeconds:对象空闲时间,超过该时间没被访问则删除
timeToLiveSeconds:对象存活时间。
maxElementsOnDisk:磁盘上缓存的元素的最大数量
diskExpiryThreadIntervalSeconds:对象检测线程运行时间间隔。
memoryStoreEvictionPolicy:内存中数据超过内存限制,则向磁盘缓存。默认:LRU(最近最少使用),FIFO(先进先出),LFU(最少使用)
pom.xml的依赖项
<dependencies>
<!-- springBoot的启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- thymeleaf的启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 测试的jar包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- springboot整合jpa需要的依赖包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- druid数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<!-- mysql数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
<!-- SpringBoot缓存启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- ehcache依赖包 -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
</dependencies>
application.yml配置文件
spring:
#配置数据源
datasource:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/ssm_crud?characterEncoding=utf-8
username: root
password: 123456
#使用druid连接池
type: com.alibaba.druid.pool.DruidDataSource
jpa:
hibernate:
#正向工程,如果数据中没有表,则会创建一张表再更新数据,存在,则会直接更新数据
ddl-auto: update
#打印执行sql语句
show-sql: true
#配置ehcache缓存的配置文件
cache:
ehcache:
config: classpath:ehcache.xml
ehcache.xml缓存机制的缓存策略
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="java.io.tmpdir"/>
<!-- defaultCache:ehcache的默认缓存策略 -->
<!--maxElementsInMemory:最大的缓存对象数量
eternal:是否持久化
timeToIdleSeconds:对象空闲时间,超过该时间没被访问则删除
timeToLiveSeconds:对象存活时间。
maxElementsOnDisk:磁盘上缓存的元素的最大数量
diskExpiryThreadIntervalSeconds:对象检测线程运行时间间隔。
memoryStoreEvictionPolicy:内存中数据超过内存限制,则向磁盘缓存。默认:LRU(最近最少使用),FIFO(先进先出),LFU(最少使用)
-->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</defaultCache>
<!-- 自定义缓存策略 -->
<cache name="users"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</cache>
</ehcache>
启动类
package com.kevin;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
/**
* @author kevin
* @version 1.0
* @description SpringBoot整合Ehcache缓存
*
* @Cacheable与@CacheEvict
* @Cacheable作用:将方法的返回值添加到Ehcache中做缓存。
* Value属性:指定ehcache配置文件中的缓存策略。如果没给定Value,则使用默认的缓存策略
* key属性:给缓存中的值起个名称,在查询时如果有名称相同的,则从已知缓存中将数据返回。
* @CacheEvict作用:清除缓存。
*
*
* @createDate 2019/3/21
*/
@SpringBootApplication
@EnableCaching // 启动缓存
public class EhcacheApplication {
public static void main(String[] args) {
SpringApplication.run(EhcacheApplication.class,args);
}
}
Users实体类
package com.kevin.entity;
import javax.persistence.*;
import java.io.Serializable;
/**
* @author kevin
* @version 1.0
* @description 用户实体类
* 想要使用缓存,需要将该对象实现Serializable接口做序列化
* @createDate 2019/3/20
*/
@Entity // 表示该类为实体类
@Table(name="t_users") // 对应数据库中的表名
public class Users implements Serializable {
@Id // 表示为主键
@GeneratedValue(strategy = GenerationType.IDENTITY) // 该主键的策略
@Column(name = "id") // 列名
private Integer id;
@Column(name = "name") // 列名
private String name;
@Column(name = "age") // 列名
private Integer age;
@Column(name = "address") // 列名
private String address;
public Users() {
}
public Users(String name, Integer age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
@Override
public String toString() {
return "Users{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
Dao层使用的是JpaRepository接口,无论使用什么持久层都使用一样的,这仅仅是一个示范
package com.kevin.dao;
import com.kevin.entity.Users;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* @author kevin
* @version 1.0
* @description JpaRepository接口的使用,对继承的父接口中的方法的返回值进行适配。
* 参数一 T :当前需要映射的实体类
* 参数二 ID :当前映射的实体类中的OID的类型
* @createDate 2019/3/20
*/
public interface UsersJpaRepository extends JpaRepository<Users,Integer> {
}
Service接口层
package com.kevin.service;
import com.kevin.entity.Users;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.util.List;
/**
* @author kevin
* @version 1.0
* @description
* @createDate 2019/3/21
*/
public interface UsersService {
List<Users> findUserAll();
Users findUserById(Integer id);
Page<Users> findUserByPage(Pageable pageable);
void saveUsers(Users users);
}
Service接口实现类,缓存的注解在该实现类中使用
package com.kevin.service.impl;
import com.kevin.dao.UsersJpaRepository;
import com.kevin.entity.Users;
import com.kevin.service.UsersService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* @author kevin
* @version 1.0
* @description UsersService接口实现类
*
* @createDate 2019/3/21
*/
@Repository
public class UserServiceImpl implements UsersService {
@Autowired
private UsersJpaRepository usersJpaRepository;
@Override
@Cacheable(value = "users")
public List<Users> findUserAll() {
return this.usersJpaRepository.findAll();
}
@Override
@Cacheable(value = "users" ) // 对当前查询的对象做缓存处理
public Users findUserById(Integer id) {
return this.usersJpaRepository.findById(id).get();
}
@Override
// 缓存的key,可以为空,按照SpEL表达式编写,如果不指定,则将方法内所有的参数组合起来
// 就算是查询同个方法,如果key中参数对象的值不一样,在缓存中找不到该key,则还是会从数据库中取出
@Cacheable(value = "users", key = "#pageable")
public Page<Users> findUserByPage(Pageable pageable) {
return this.usersJpaRepository.findAll(pageable);
}
@Override
@CacheEvict(value = "users", allEntries = true) // 清除缓存中以users缓存策略缓存的对象
public void saveUsers(Users users) {
this.usersJpaRepository.save(users);
}
}
测试Ehcache缓存,该测试使用SpringBoot中的Test
package com.kevin.test;
import com.kevin.EhcacheApplication;
import com.kevin.entity.Users;
import com.kevin.service.UsersService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author kevin
* @version 1.0
* @description 测试ehcache缓存
* @createDate 2019/3/21
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = EhcacheApplication.class)
public class UserServiceTest {
@Autowired
private UsersService usersService;
// ehcache缓存测试@Cacheable,默认key 在findUserById查询方法中加入缓存,第一次查询时查询了数据库,第二次直接从缓存取出
@Test
public void testFindUserById(){
// 第一次查询
Users user = this.usersService.findUserById(1);
System.out.println(user);
// 第二次查询
Users user2 = this.usersService.findUserById(1);
System.out.println(user2);
}
// ehcache缓存测试@Cacheable,自定义key
@Test
public void testFindUserByPage(){
// 设置分页
Pageable pageable = new PageRequest(0,2);
// 第一次查询
Page<Users> user = this.usersService.findUserByPage(pageable);
System.out.println(user.getTotalElements());
for (Users u:user) {
System.out.println(u);
}
// 第二次查询
Page<Users> user1 = this.usersService.findUserByPage(pageable);
System.out.println(user.getTotalElements());
for (Users u:user1) {
System.out.println(u);
}
// 第三次查询
pageable = new PageRequest(1, 2);
Page<Users> user2 = this.usersService.findUserByPage(pageable);
System.out.println(user.getTotalElements());
for (Users u:user2) {
System.out.println(u);
}
}
// ehcache缓存测试@CacheEvict,清空缓存
@Test
public void testFindAll(){
// 第一次查询,查询数据记录长度
System.out.println(this.usersService.findUserAll().size());
// 增加,长度加一
Users users = new Users();
users.setName("小波");
users.setAge(24);
users.setAddress("湛江");
this.usersService.saveUsers(users);
// 第二次查询,如果不清除缓存,则还是取到之前的数据,所以需要将缓存清除
System.out.println(this.usersService.findUserAll().size());
}
}