SpringBoot学习——springboot整合Ehcache实现数据缓存

版权声明:转载请注明来源 https://blog.csdn.net/qq_24598601/article/details/89281673

  SpringBoot 整合 Encache 实现数据的缓存,本次使用技术是基于 SpringBoot 整合 Spring JPA 进行测试。通过配置 spring.jpa.show-sql=true 显性的观察到是否实现了数据的缓存,从而验证是否整合成功。

一、POM 文件

  在 pom.xml 文件中添加 spring-boot-starter-cache 的坐标,使 SpringBoot 有支持 Encache 的基本Jar 环境。

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

二、创建 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">
	
	<!--默认缓存策略 -->
	<defaultCache maxElementsInMemory="10000" eternal="false"
		timeToIdleSeconds="120" timeToLiveSeconds="120"
		maxElementsOnDisk="10000" diskExpiryThreadIntervalSeconds="120"
		memoryStoreEvictionPolicy="LRU">
		<persistence strategy="localTempSwap" />
	</defaultCache>
	
	<!-- 自定义缓存策略 -->
	<cache name="users"
		maxElementsInMemory="10000" 
		eternal="false"
		timeToIdleSeconds="120" 
		timeToLiveSeconds="120"
		maxElementsOnDisk="10000" 
		diskExpiryThreadIntervalSeconds="120"
		memoryStoreEvictionPolicy="LRU">
		
		<persistence strategy="localTempSwap" />
	</cache>
</ehcache>

   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 文件

  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 和 Service

  创建 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<Users, String> {
	//需要使用其他的在这里定义方法
	
}

  创建 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<Users> 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<Users> 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

  编写一个测试 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<Users> 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<Users> result = null;
		if(valueWrapper != null) {
			result = (List<Users>)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<Users>)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<Users>)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 注解也可清空缓存。

八、总结

  1. Springboot整合Encache实现数据缓存时,可以通过注入 CacheManager 实现缓存内容的查询和缓存清空;
  2. 可以使用 @Cacheable@CacheEvict 实现缓存和缓存清空;
  3. 清空缓存有两种方式,方式一通过使用 cache.clear();,方式二使用 @CacheEvict 注解在调用指定方法时清空缓存;

猜你喜欢

转载自blog.csdn.net/qq_24598601/article/details/89281673