preface
Caching technology is essential in actual projects. Reasonable use of caching technology can greatly improve the website access speed and improve user experience. This article explains how to use ehCache, a caching framework, in Spring Boot.
This article appeared on my personal blog: [www.xiongfrblog.cn]
Ehcache is introduced
There are many techniques to implement caching in Java. The simplest is to use Java’s own Map container, or to use existing caching frameworks such as Memcache, Ehcache, and the popular Redis. Ehcache is introduced here because it is really convenient, and memcache and Redis both require additional setup services, which are more suitable for distributed deployment projects so that common cache content can be used between modules. Ehcache is mainly a memory cache, but also can be cached to disk, fast, efficient, powerful, suitable for our general single project use.
Ehcache is configured in Spring Boot
There are four steps to configure ehCAhce in Spring Boot:
pom.xml
Add a dependency to- configuration
ehcache.xml
The configuration file - Open the cache
- Use caching with annotations
Here we go over each step in detail.
Add the dependent
To use caching in Spring Boot, we first need to enable caching and then add ehCache dependencies, so we add the following dependencies in pom.xml:
<! -- Enable 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>
Copy the code
Writing a configuration file
When dependencies are added, Spring Boot automatically loads the ehcache. XML file in SRC /mian/resources, so we need to manually create this file in that directory. Here is an example:
<?xml version="1.0" encoding="UTF-8"? >
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<! -- Disk cache file path -->
<diskStore path="java.io.tmpdir"/>
<! -- default Settings -->
<defaultCache eternal="false"
maxElementsInMemory="1000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="0"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"/>
<! -- Custom configuration -->
<cache name="userCache"
eternal="false"
maxElementsInMemory="1000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="0"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
Copy the code
The following three nodes appear in the sample:
<diskStore>
: This node is optional and needs to be configured only when disk storage is used. It indicates the path for storing cached files on the diskpath
Property to specify that the file name suffix used by disk caching is*.data
and*.index
, mainly has the following values:user.home
: Home directory of the useruser.dir
: Indicates the current working directory of the userjava.io.tmpdir
: Default temporary pathehcache.disk.store.dir
: Cache configuration directory- Custom absolute path
If you are not familiar with these directories, you can obtain them in Java as follows:
public static void main(String[] args) {
System.out.println(System.getProperty("user.home"));
System.out.println(System.getProperty("user.dir"));
System.out.println(System.getProperty("java.io.tmpdir"));
}
Copy the code
The following is the path printed by my machine for reference only:
C:\Users\Administrator
D:\Program Data\eclipse-workspace\springboot-ehcache
C:\Users\Administrator\AppData\Local\Temp\
Copy the code
One thing to note here is that for an object to be cached to disk, it needs to implement a serialization interface.
-
: custom cache, which can have zero or more. Important attributes are as follows:
-
Name: The name of the cache, a mandatory attribute that uniquely identifies the cache.
-
Eternal: sets whether the contents in the cache are permanent. The optional value is true or false. If true is selected, the timeToIdleSeconds and timeToLiveSeconds set will be invalid.
-
MaxElementsInMemory: The maximum number of objects that can be stored in this cache, beyond which operations are performed based on the value of the overflowToDisk attribute.
-
OverflowToDisk: Whether to enable disk saving when the number of cache objects exceeds the maximum. The value can be true or false. If the value is true, the excess content will be cached to the disk. Will be based on false memoryStoreEvictionPolicy attribute configuration strategy to replace the original content.
-
DiskPersistent: Indicates whether the disk storage persists after the VM restarts. The default value is False. If the value is true, the system loads the disk contents to the cache during initialization.
-
TimeToIdleSeconds: Sets the maximum amount of time for an element to be free (in seconds) before it expires before the element is cleared. The default value is 0, which means an element can be free indefinitely.
-
TimeToLiveSeconds: sets the lifetime of an element in the cache in seconds. This is the amount of time it takes for the element to be created and cleared. The default value is 0, indicating that an element can be saved indefinitely.
-
MemoryStoreEvictionPolicy: cache storage and clear strategy. When the maxElementsInMemory limit is reached and the overflowToDisk value is false, ehCache will execute the corresponding policy based on the value of this attribute. This attribute has three values representing ehCache’s three cache clearing policies. The default value is LRU:
FIFO
: First in first out strategy(First In First Out)
.LFU
: Least used(Less Frequently Used)
, all cached elements will have an attribute that records the number of times the element has been used, and the smallest element will be cleared.LRU
: Least recently used(Least Resently Used)
, all cached elements will have an attribute that records the last time they were used, and the element with the earliest time will be cleared.
-
DiskExpiryThreadIntervalSeconds: disk cache cleaning thread running interval, default is 120 seconds.
-
DiskSpoolBufferSizeMB: Sets the size of the disk cache. The default size is 30MB.
-
MaxEntriesLocalDisk: Sets the maximum number of elements that the disk cache can store.
-
-
: the default cache, that is, an
node whose name attribute is default, has the same attributes as the
node, and can only have one
node in an ehcache. XML file. This cache is used by default when we do not have a custom
.
It is important to note that defaultCache is special, so we can no longer define a cache named default, and we cannot specify a default cache using value=default when using it.
If you do not want to use the default path and name for your project, you can also customize the ehcache configuration file name and path by configuring the following in the application. Properties configuration file:
The path after # can be specified by itself
spring.cache.ehcache.config=classpath:ehcache.xml
Copy the code
Open the cache
Turning on caching in Spring Boot is as simple as adding an @enablecaching annotation to the boot class.
Using annotations
In Spring Boot, ehCache is used mainly through annotations, and we usually use caching in the service implementation layer. The common annotations are:
@Cacheable
The annotations mainly use the method above, when applications into the annotations of the tag method, the system will first determines whether there is the same key element in the cache, if there’s a direct return values stored in the buffer zone, and not the content of the execution method, if there is no execute this method, and determine whether need to add a return value to the buffer, the commonly used attributes:
value
: Specifies which cache to use, which we set in the configuration file<ehcache>
The node’sname
Property. Multiple values can be specified.// Specify one @Cacheable(value="userCache") // Specify multiple @Cacheable(value={"userCache"."userCache2"}) Copy the code
key
: of the cached elementkey
In accordance withSpEL
Expression, which we usually determine in terms of the parameters to the specified method.//#p0 = "key"; //#p0 = "key" @Cacheable(value="userCache",key="#p0") public SysUser getById(Integer id){// This is not... }; Copy the code
condition
: Specifies the conditions for adding a cacheSpEL
Expression is written only if the property is returnedtrue
When the cache is added.// Cache only when id>10 @Cacheable(value="userCache",key="#p0",condition="#p0>10") public SysUser getById(Integer id){// This is not... }; Copy the code
@CachePut
This annotation is used primarily on methods, and can be used to determine whether to add caching based on the parameters and return values of the method and custom conditions. The method marked by this annotation will be executed, and its properties are consistent with @cacheable.
@CachePut(value="userCache",key="#entity.id")
public SysUser insertSysuser(SysUser entity) {
// TODO Auto-generated method stub
// Omit content
}
Copy the code
@CacheEvict
This annotation is mainly used for methods and can be used to clear the cache based on conditions. The common properties are as follows:
value
: same as abovekey
: same as abovecondition
: same as aboveallEntries
: Whether to clear all cache contents. The default value isfalse
If the value is set totrue
, then after the method is executed and satisfiescondition
Condition will clear the contents of the cache.beforeInvocation
: Whether the clear action occurs before the method is executed. The default isfalse
, indicates that the clear operation is performed after the method is executed. If an exception is thrown during the execution of the method, the clear operation is not performedtrue
, the clear operation is performed before the method is executed.
@CacheEvict(value="userCache",key="#p0",allEntries=false, beforeInvocation=true)
public int deleteByPrimarykey(Integer key) {
// TODO Auto-generated method stub
// Omit content
}
Copy the code
Effect of the test
This project is carried out on the premise of integrating Mybatis and logging framework. The basic code will not be published, and directly give the most critical service implementation layer and controller code:
SysuserServiceImpl.java
package com.web.springbootehcache.service.impl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import com.web.springbootehcache.dao.SysUserMapper;
import com.web.springbootehcache.entity.SysUser;
import com.web.springbootehcache.service.IsysUserService;
/ * * *@author Promise
* @createTime19 March 2019 *@description* /
@Service("sysuserService")
public class SysUserServiceImpl implements IsysUserService{
private final static Logger log = LoggerFactory.getLogger(SysUserServiceImpl.class);
@Autowired
private SysUserMapper sysuserMapper;
@Override
@Cacheable(value="userCache",key="#p0")
public SysUser fingByPrimarykey(Integer key) {
// TODO Auto-generated method stub
log.debug("Checked the data in the database!");
return sysuserMapper.selectByPrimaryKey(key);
}
@Override
@CachePut(value="userCache",key="#p0.id")
public SysUser updateSysuser(SysUser entity) {
// TODO Auto-generated method stub
log.debug("Database data updated!");
int res = sysuserMapper.updateByPrimaryKey(entity);
if(res >0)
return entity;
else
return null;
}
@Override
@CachePut(value="userCache",key="#entity.id")
public SysUser insertSysuser(SysUser entity) {
// TODO Auto-generated method stub
int res = sysuserMapper.insert(entity);
log.debug("New data! Id is: {}",entity.getId());
if(res >0)
return entity;
else
return null;
}
@Override
@CacheEvict(value="userCache",key="#p0",beforeInvocation=true)
public int deleteByPrimarykey(Integer key) {
// TODO Auto-generated method stub
log.debug("Deleted data!");
returnsysuserMapper.deleteByPrimaryKey(key); }}Copy the code
This class provides the basic CRUD operation corresponding to the cache operation, of course, is not absolute, the actual use according to their own needs to change.
IndexController.java
package com.web.springbootehcache.controller;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.web.springbootehcache.entity.SysUser;
import com.web.springbootehcache.service.IsysUserService;
/ * * *@author Promise
* @createTime19 March 2019 *@description* /
@RestController
public class IndexController {
private final static Logger log = LoggerFactory.getLogger(IndexController.class);
@Autowired
private IsysUserService sysuserService;
@RequestMapping(value="/select/{id}")
public Object index(@PathVariable Integer id) {
Map<String, Object> map = new HashMap<>();
SysUser sysuser = sysuserService.fingByPrimarykey(id);
log.debug("Query user id :{}",sysuser.getId());
SysUser sysuser2 = sysuserService.fingByPrimarykey(id);
log.debug("Query user id :{}",sysuser2.getId());
map.put("res", sysuser);
return map;
}
@RequestMapping(value="/update")
public Object update(a) {
Map<String, Object> map = new HashMap<>();
// First modification
SysUser sysuser = new SysUser(1."eran"."eran1".20."M");
sysuserService.updateSysuser(sysuser);
// The first query
sysuser = sysuserService.fingByPrimarykey(1);
log.debug("Query user id :{}",sysuser.getId());
// Second modification
sysuser = new SysUser(1."eran"."eran2".20."M");
sysuserService.updateSysuser(sysuser);
// The second query
sysuser = sysuserService.fingByPrimarykey(1);
log.debug("Query user id :{}",sysuser.getId());
map.put("res", sysuser);
return map;
}
@RequestMapping(value="/insert")
public Object insert(a) {
Map<String, Object> map = new HashMap<>();
SysUser sysuser = new SysUser();
sysuser.setName("admin");
sysuser.setAge(22);
sysuser.setPass("admin");
sysuser.setSex("M");
sysuserService.insertSysuser(sysuser);
/ / query
sysuser = sysuserService.fingByPrimarykey(sysuser.getId());
map.put("res", sysuser);
return map;
}
@RequestMapping(value="/delete/{id}")
public Object delete(@PathVariable Integer id) {
Map<String, Object> map = new HashMap<>();
sysuserService.deleteByPrimarykey(id);
/ / query
SysUser sysuser = sysuserService.fingByPrimarykey(id);
map.put("res", sysuser);
returnmap; }}Copy the code
Database test data
Start project, access localhost:1188/select/1, console log as follows:
Expected result: The query is performed twice and the database is accessed once.
[default]2019-03-20 17:35:05,287 [http-nio-1188-exec-2 32] [default]2019-03-20 17:35:05,287 [http-nio-1188-exec-2 32] > > C.W.S.S.I.S ysUserServiceImpl [default] 2019-03-20 17:35:05, 327 [HTTP - nio 2-1188 - exec - 110] INFO > > HikariPool - 1 - Starting... >> c.z.h.hikaridatasource [default]2019-03-20 17:35:05,332 [HTTP-NiO-1188-exec-2 68] WARN >> Registered driver with driverClassName=com.mysql.jdbc.Driver was not found, Trying direct instantiation. >> C.Z.H.U.D riverDataSource [default]2019-03-20 17:35:06,106 [http-niO-1188-exec-2 123] INFO >> hikaripool1-start completed. >> c.z.h.hikaridatasource [default]2019-03-20 17:35:06,113 [HTTP-nio-1188-exec-2 159] DEBUG >> ==> Preparing: select id, `name`, pass, sex, age from sys_userwhereid = ? > > C.W.S.D.S.S electByPrimaryKey [default] 2019-03-20 17:35:06, 141 [HTTP - nio 2-1188 - exec - 159] the DEBUG > > = = > the Parameters: 1 (Integer) > > C.W.S.D.S.S electByPrimaryKey [default] 2019-03-20 17:35:06, 176 [HTTP - nio 2-1188 - exec - 159] the DEBUG > > < = = Total: 1 > > C.W.S.D.S.S electByPrimaryKey [default] 2019-03-20 17:35:06, 185 [HTTP - nio 2-1188 - exec - 33] the DEBUG > > query with id: 1 user information! >> C.W.S.C.I ndexController [default]2019-03-20 17:35:06,186 [http-nio-1188-exec-2 35] DEBUG >> Query the user id :1! >> c.w.s.c.IndexControllerCopy the code
As you can see, we performed two queries, but only once to fetch data from the database, and once to fetch data directly from the cache, which achieved the desired effect.
Update = localhost:1188/update = localhost:1188/update = localhost:1188/update = localhost:1188/update = localhost:1188/update = localhost:1188/update = localhost:1188/update = localhost:1188/update = localhost:1188/update = localhost:1188/update = localhost:1188/update = localhost:1188/update = localhost:1188/update = localhost:1188
Expected result: After two modification operations are performed, the database is accessed twice. After two query operations, the database is not accessed.
[default]2019-03-20 17:47:37,254 [http-nio-1188-exec-1 40] [default]2019-03-20 17:47:37,254 [http-nio-1188-exec-1 40] [default]2019-03-20 17:47:37,254 [http-nio-1188-exec-1 40] > > C.W.S.S.I.S ysUserServiceImpl [default] 2019-03-20 17:47:37, 291 [HTTP - nio - 1188 - exec - 1, 110] INFO > > HikariPool - 1 - Starting... >> c.z.h.hikaridatasource [default]2019-03-20 17:47:37,299 [HTTP-NiO-1188-exec-1 68] WARN >> Registered driver with driverClassName=com.mysql.jdbc.Driver was not found, Trying direct instantiation. >> C.Z.H.U.D riverDataSource [default]2019-03-20 17:47:3753 [http-niO-1188-exec-1 123] INFO >> hikaripool1-start completed. >> c.z.h.hikaridatasource [default]2019-03-20 17:47:3764 [http-nio-1188-exec-1 159] DEBUG >> ==> Preparing: update sys_userset`name` = ? , pass = ? , sex = ? , age = ?whereid = ? > > C.W.S.D.S.U pdateByPrimaryKey [default] 2019-03-20 17:47:38, 006 [HTTP - nio - 1188 - exec - 1, 159] the DEBUG > > = = > the Parameters: eran(String), eran1(String), M(String), 20(Integer), 2 (Integer) > > C.W.S.D.S.U pdateByPrimaryKey [default] 2019-03-20 17:47:38, 104 [HTTP - nio - 1188 - exec - 1, 159] the DEBUG > > < = = Updates: 1 > > C.W.S.D.S.U pdateByPrimaryKey [default] 2019-03-20 17:47:38, 237 [HTTP - nio - 1188-48 exec - 1] DEBUG > > query with id: 2 user information! >> C.W.S.C.I ndexController [default]2019-03-20 17:47:38,239 [http-nio-1188-exec-1 40] DEBUG >> > > C.W.S.S.I.S ysUserServiceImpl [default] 2019-03-20 17:47:38, 239 [HTTP - nio - 1188 - exec - 1, 159] the DEBUG > > = = > Preparing: update sys_userset`name` = ? , pass = ? , sex = ? , age = ?whereid = ? > > C.W.S.D.S.U pdateByPrimaryKey [default] 2019-03-20 17:47:38, 243 [HTTP - nio - 1188 - exec - 1, 159] the DEBUG > > = = > the Parameters: eran(String), eran2(String), M(String), 20(Integer), 2 (Integer) > > C.W.S.D.S.U pdateByPrimaryKey [default] 2019-03-20 17:47:38, 286 [HTTP - nio - 1188 - exec - 1, 159] the DEBUG > > < = = Updates: 1 > > C.W.S.D.S.U pdateByPrimaryKey [default] 2019-03-20 17:47:38, 287 [HTTP - nio - 1188-54 exec - 1] DEBUG > > query with id: 2 user information! >> c.w.s.c.IndexControllerCopy the code
The results were in line with our expectations.
CachePut = @cacheput = @cacheput = @cacheput = @cacheput = @cacheput = @cacheput = @cacheput = @cacheput
Expected effect: The database accesses the deleted data once, and the corresponding data in the cache is cleared. The query data accesses the database once because the contents of the cache are cleared, but the corresponding contents in the database are also deleted, so no data can be queried.
[default]2019-03-20 17:57:28,337 [http-nio-1188-exec-4 64] DEBUG >> delete data > > C.W.S.S.I.S ysUserServiceImpl [default] 2019-03-20 17:57:28, 341 [HTTP - nio - 1188 - exec - 4 159] the DEBUG > > = = > Preparing: delete from sys_userwhereid = ? > > C.W.S.D.S.D eleteByPrimaryKey [default] 2019-03-20 17:57:28, 342 [HTTP - nio - 1188 - exec - 4 159] the DEBUG > > = = > the Parameters: 2 (Integer) > > C.W.S.D.S.D eleteByPrimaryKey [default] 2019-03-20 17:57:28, 463 [HTTP - nio - 1188 - exec - 4 159] the DEBUG > > < = = Updates: 1 > > C.W.S.D.S.D eleteByPrimaryKey [default] 2019-03-20 17:57:28, 464 [HTTP - nio - 1188-32 exec - 4] the DEBUG > > data to a database query! > > C.W.S.S.I.S ysUserServiceImpl [default] 2019-03-20 17:57:28, 467 [HTTP - nio - 1188 - exec - 4 159] the DEBUG > > = = > Preparing: select id, `name`, pass, sex, age from sys_userwhereid = ? > > C.W.S.D.S.S electByPrimaryKey [default] 2019-03-20 17:57:28, 468 [HTTP - nio - 1188 - exec - 4 159] the DEBUG > > = = > the Parameters: 2 (Integer) > > C.W.S.D.S.S electByPrimaryKey [default] 2019-03-20 17:57:28, 494 [HTTP - nio - 1188 - exec - 4 159] the DEBUG > > < = = Total: 0 >> c.w.s.d.S.selectByPrimaryKeyCopy the code
The log output meets our expectations.
conclusion
So much for Integrating EhCache with Spring Boot. See you in the next blog