This is the second day of my participation in the August More text Challenge. For details, see:August is more challenging
I. Cache Functions of the Cache
With the accumulation of time, the number of users of the application is increasing, and the scale of data is also getting bigger and bigger. Often, database query operation will become the bottleneck of user experience. At this time, cache is often a very good way to solve this problem. Using annotations to add caching to existing Sping applications with low intrusion to improve data access performance, Springboot provides a series of automated configurations for caching, making it easy to use.
1. Main core classes
Java Caching defines five core interface, respectively is CachingProvider, CacheManager that Cache, Entry and Expiry.
CachingProvider defines how to create, configure, obtain, manage, and control multiple CacheManager managers. An application can access multiple CachingProviders at run time.
CacheManager defines the creation, configuration, acquisition, management, and control of multiple uniquely named caches that exist in the context of CacheManager. A CacheManager is owned by only one CacheProvider.
A Cache is a data structure similar to a Map that temporarily stores values indexed by keys. A Cache is owned by only one CacheManager.
An Entry is a key-value pair stored in the Cache.
Expiry Each entry stored in Cache has a defined period of validity. Once this period is exceeded, the entry is in the status of expired and once expired, the entry cannot be accessed, updated or deleted. The cache expiration date can be set by ExpiryPolicy.
2. Spring cache objects
Spring since 3.1 defines the org. Springframework. Cache. The cache and org., springframework. Cache. CacheManager unified interface to different caching technology, And support for using JCache(JSR-107) annotations to simplify our development.
The Cache interface is defined by the Cache component specification and contains a set of various operations for the Cache.
Cache interface under Spring provides various xxxCache implementation, such as RedisCache EhCacheCache, ConcurentMapCache.
Each time a method that requires caching is called, Spring checks to see if the specified target method with the specified argument has been called. If so, it retrieves the result of the method call directly from the cache. If not, it calls the method and returns the result to the user, and the next call retrieves it directly from the cache.
There are two things to keep in mind when using Spring cache abstraction:
1. Determine the methods that need to be cached and their caching strategy
2. Read the data from the previous cache from the cache
Two, several important concepts & cache annotations
Concept & Annotation | role |
---|---|
Cache | Cache interface, define cache operation, implement RedisCache, EhCacheCache, ConcurrentMapCache, etc |
CacheManager | Cache manager, which manages various Cache components |
@Cacheable | Mainly for method configuration, the results of methods can be cached based on their request parameters |
@CacheEvict | Clear the cache |
@CachePut | Ensure that the method is called and expect the result to be cached |
@EnableCaching | Turn on annotation-based caching |
keyGenerator | Key generation policy when caching data |
serialize | Value serialization policy when caching data |
Cache SpEL available metadata
The name | location | describe | The instance |
---|---|---|---|
methodName | root object | The name of the method currently being called | #root,methodName |
method | root object | The method currently being called | root method name |
target | root object | The target object currently being called | root target |
targetClass | root object | The target object class that is currently being called | root targetClass |
args | root object | The argument list of the currently invoked method | root args[0] |
caches | root object | The list of caches used by the current method call (such as @cacheable (value={“cache1″,”cache2”})), then there are two CachOs | root caches[0]name |
argument name | evaluation context | The name of the method parameter, either directly # parameter name, or in the form #p0 or # A0, where 0 represents the index of the parameter | #a0,#P0 |
result | evaluation context | Return value after method invocation (only if the judgment after method invocation is valid, e.g. ‘unless’,’cache put’ expression ‘cache EVICT ‘expression =false) | result |
3. Use of Cache in Springboot, and store Cache in Redis
First, the project directory:
Add dependencies first
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
Copy the code
Add the configuration to the main startup class to enable caching
package com.sense; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; @SpringBootApplication @MapperScan(basePackages = "com.sense.dao") @EnableCaching public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }}Copy the code
Creating an entity class
package com.sense.entity;
import lombok.Data;
/**
@desc ...
@date 2021-07-20 14:37:35
@author
*/
@Data
public class User {
private int id;
private String username;
private int password;
}
Copy the code
Create dao
package com.sense.dao;
import com.sense.entity.User;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
@desc ...
@date 2021-08-02 10:42:33
@author
*/
@Mapper
public interface UserDao {
List<User> getUser();
User getUserById(Integer id);
void update(User user);
void deleteUser(Integer id);
User queryByName(String name);
}
Copy the code
Create a service
package com.sense.service;
import com.sense.entity.User;
import java.util.List;
/**
@desc ...
@date 2021-08-02 10:37:24
@author
*/
public interface UserService {
List<User> getUser();
User getUserById(Integer id);
void update(User user);
void deleteUser(Integer id);
User queryByName(String name);
}
Copy the code
Create impl
package com.sense.service.impl; import com.sense.dao.UserDao; import com.sense.entity.User; import com.sense.service.UserService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.*; import org.springframework.stereotype.Service; import java.util.List; /** @desc ... @date 2021-08-02 10:40:48@author */ @service @slf44j @cacheconfig (cacheNames = "user" UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Override public List<User> getUser() { return userDao.getUser(); } /** * @desc@cacheable: cacheNames/value: specifies the name of the component concerned * Key: specifies the key used to cache data. * cacheManager and cacheResolver function the same way. Specify the cacheManager. Either of the following options is available: * condition: If (condition="#id>3"); if (condition="#id>3"); if (condition="#id>3"); Whether to use asynchronous mode * @ param * @ return Java. Util. List < com. Sense. Dingtalkdemo. Entity. The User > * * @ date 2021/7/30 then * / @ Override @cacheable (cacheNames = "user",key = "#id") public user getUserById(Integer ID) {log.info(" get "+id+" info "); return userDao.getUserById(id); } /** * @desc @cacheput must be used in conjunction with @cacheable, otherwise it makes no sense * @cacheput: Call method, update cache data * modify database data, and update cache * run time: * 1, call the target method, * 2, cache the result of the target method return 1 * test step: * 1. Query # 1's personal information * 2, query # 1's personal information * 3, update # 1's personal information * 4, query # 1's personal information * 4, return the result * should be updated employee * but only update the database, but not update the cache why? * 5, * @cachePUT (cacheNames="user",key="#user.id") * @cachePUT (cacheNames="user",key="#result.id") * @param user 1 * @return com.sense.dingtalkdemo.entity.User * * @date 2021/7/30 13:47 */ @Override @CachePut(cacheNames = "User ",key = "#result.id") public void update(user user) {system.out.println (" modify "+ user.getid ()+" information "); userDao.update(user); } /** * @descac@cacheevict: Clear the cache * 1.key: Specify the data to be cleared * 2.allEntries=true: Delete all data in the cache ** ** @descac@cacheevict: Clear the cache * 1.key: Specify the data to be cleared * 2.allEntries=true * beforeInvocation=true; Clearing the cache before the method is executed Only clears the cache, * @param ID 1 * @return void * * @date 2021/7/30 14:10 */ @override // @cacheevict (cacheNames = "user",key = "#id") @CacheEvict(cacheNames = "user",allEntries = true) public void deleteUser(Integer id){ System.out.println(" delete "+id+" info "); Userdao.deleteuser (id); userdao.deleteUser (id); /** * @desc beforeInvocation=true */ / Int a=1/0; /** * @desc beforeInvocation= True */ } /** * @desc;} /** * @desc;} Now, if I use id,age is going to query the cache directly, Instead of querying the database * @ param name 1 * @ return com. Sense. Dingtalkdemo. Entity. The User * * @ date 2021/7/30 * / @ Override to them @Caching(cacheable = {@Cacheable(value = "user",key = "#name")}, put = { @CachePut(value = "user",key = "#result.id"), @CachePut(value = "user",key = "#result.password")}) public User queryByName(String name) { System.out.println(" query name "+name); return userDao.queryByName(name); }}Copy the code
Creating the config
package com.sense.config; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.time.Duration; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** @desc ... @date 2021-08-02 09:39:44 @author */ @Configuration public class RedisConfig extends CachingConfigurerSupport { /** * @desc Configure a custom redisTemplate * @param connectionFactory 1 * @return org.springframework.data.redis.core.RedisTemplate<java.lang.String, java.lang.Object> */ @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); template.setValueSerializer(jackson2JsonRedisSerializer()); / / use StringRedisSerializer to serialization and deserialization redis key values in the template. SetKeySerializer (new StringRedisSerializer ()); template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(jackson2JsonRedisSerializer()); template.afterPropertiesSet(); return template; @ desc} / * * * * @ param * @ return json serialization org. Springframework. Data. Redis. Serializer. RedisSerializer <? > * * @date 2021/8/2 9:51 */ @Bean public RedisSerializer<? > jackson2JsonRedisSerializer () {/ / use jackson2JsonRedisSerializer to serialization and deserialization value of Redis values Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<Object>(Object.class); ObjectMapper mapper = new ObjectMapper(); mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); serializer.setObjectMapper(mapper); return serializer; } / * * * * @ @ desc configuration cache manager param redisConnectionFactory 1 * @ return org. Springframework. Cache. CacheManager * / @ Bean public CacheManager CacheManager (RedisConnectionFactory RedisConnectionFactory) {// generate a default configuration, Through the config object can cache for custom configuration RedisCacheConfiguration config. = RedisCacheConfiguration defaultCacheConfig (); // Set the default expiration time of the cache, Config = config.entryTTL (duration.ofminutes (1)) // Set key to string serialization .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) // Set value to json serialization SerializeValuesWith (RedisSerializationContext. SerializationPair. FromSerializer (jackson2JsonRedisSerializer ())) / / no cache a null value .disableCachingNullValues(); Set<String> cacheNames = new HashSet<>(); cacheNames.add("timeGroup"); cacheNames.add("user"); // Apply a different configuration for each cache space Map<String, RedisCacheConfiguration> configMap = new HashMap<>(); configMap.put("timeGroup", config); configMap.put("user", config.entryTtl(Duration.ofSeconds(120))); / / the use of a custom cache configuration initialization a cacheManager RedisCacheManager cacheManager = RedisCacheManager. Builder (redisConnectionFactory) / / must call this method first sets the initialization of the cache, to initialize the relevant configuration. InitialCacheNames (cacheNames). WithInitialCacheConfigurations (configMap). The build (); return cacheManager; } / * * * @ desc cache key is a package name + method name and argument list * @ return org. Springframework. Cache. The interceptor. @ date 2021/8/2 KeyGenerator * and * / @Override @Bean public KeyGenerator keyGenerator() { return (target, method, objects) -> { StringBuilder sb = new StringBuilder(); sb.append(target.getClass().getName()); sb.append("::" + method.getName() + ":"); for (Object obj : objects) { sb.append(obj.toString()); } return sb.toString(); }; }}Copy the code
Creating an XML file
<? The XML version = "1.0" encoding = "utf-8"? > <! DOCTYPE mapper PUBLIC "- / / mybatis.org//DTD mapper / 3.0 / EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > < mapper namespace="com.sense.dao.UserDao"> <select id="getUser" resultType="com.sense.entity.User"> select * from user </select> <select id="getUserById" resultType="com.sense.entity.User"> select * from user where id = #{id} </select> <update id="update" parameterType="com.sense.entity.User"> update user set username=#{username}, password=#{password} where id = #{id} </update> <delete id="deleteUser" parameterType="java.lang.Integer"> delete from user where id=#{id} </delete> <select id="queryByName" resultType="com.sense.entity.User"> select * from user where username = #{username} </select> </mapper>Copy the code
The introduction of Ehcache
Some notes about Ehcahe:
- Name: Indicates the cache name.
- MaxElementsInMemory: Indicates the maximum number of cache
- MaxElementsOnDisk: indicates the maximum number of cached disks.
- Eternal: whether the object is permanent, once set, timeout will have no effect.
- OverflowToDisk: whether to save to a disk.
- TimeToIdleSeconds: Sets the amount of time (in seconds) an object is allowed to be idle before it expires. Only when the
eternal=false
An optional property used when the object is not permanently valid. The default value is 0, which means that the idle time is infinite. - TimeToLiveSeconds: Sets the amount of time (in seconds) an object can live before it expires. The maximum time is between the creation time and the expiration time. Only when the
eternal=false
This is used when the object is not permanent. The default value is 0, which means the object has an infinite lifetime. - DiskPersistent: specifies whether to cache vm data during vm restart. The default value is false.
- DiskSpoolBufferSizeMB: This parameter sets the cache size of the DiskStore (disk cache). The default is 30MB. Each Cache should have its own buffer.
- DiskExpiryThreadIntervalSeconds: disk failure thread running time interval, the default is 120 seconds.
- MemoryStoreEvictionPolicy: when maxElementsInMemory limit is reached, Ehcache will be based on the specified strategies to clear the memory. The default policy is LRU (least recently used). You can set it to FIFO (first in, first out) or LFU (less used).
- ClearOnFlush: specifies whether to flush the memory when the memory quantity reaches the maximum.
- MemoryStoreEvictionPolicy: Ehcache three clear strategy: FIFO, the first in first out, this is everybody’s most familiar, the first in first out. LFU, Less Frequently Used, is the strategy Used in the example above, which is the least Frequently Used. As mentioned above, cached elements have a hit attribute, and the one with the lowest hit value will be cleared out of the cache. LRU, Least Recently Used, the cached element has a timestamp. When the cache is full and needs to make room for new elements, the element whose timestamp is furthest from the current time will be cleared from the cache.
Import dependence
<! -- ehcache --> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> </dependency>Copy the code
Creating an XML file
<? The 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 maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" /> <cache name="user" maxEntriesLocalHeap="2000" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false" statistics="true"/> </ehcache>Copy the code
Yml configuration
spring: datasource: url: jdbc:mysql://localhost:3306/test? allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.cj.jdbc.Driver username: Root password: root redis: host: 127.0.0.1 port: 6379 jedis: pool: Max wait: -1 # Max idle: 8 # Min idle: 0 cache: ehcache: config: ehcache.xmlCopy the code
For Ehcache, the update method is the same with or without the @cachePUT annotation.
The data format stored in Redis is as follows