SpringBoot cache management
Default cache management
The Spring framework supports transparently adding caches to applications to manage caches. The core of managing caches is to apply caches to methods of manipulating data to reduce the number of times the data is executed without causing any interference to the program itself.
Spring Boot inherits the cache management function of the Spring framework. By using the @enablecaching annotation to enable the annotated cache support, Spring Boot can enable automatic configuration of cache management.
Basic environment construction
1. Data preparation
Create databaseCREATEDATABASE springbootdata; USE springBootData; Create table T_article and insert related dataDROP TABLE IF EXISTS t_article;
CREATE TABLE t_article (
id INT ( 20 ) NOT NULL AUTO_INCREMENT COMMENT 'the article id',
title VARCHAR ( 200 ) DEFAULT NULL COMMENT 'Article Title',
content LONGTEXT COMMENT 'Article content'.PRIMARY KEY ( id )
) ENGINE = INNODB AUTO_INCREMENT = 2 DEFAULT CHARSET = utf8;
INSERT INTO t_article VALUES ('1'.'Spring Boot Basics'.'From entry to mastery... ');
INSERT INTO t_article VALUES ('2'.'Spring Cloud Basics'.'From entry to mastery... '); Create table T_COMMENT and insert related dataDROP TABLE IF EXISTS t_comment;
CREATE TABLE t_comment (
id INT ( 20 ) NOT NULL AUTO_INCREMENT COMMENT 'comment id',
content LONGTEXT COMMENT 'Comment content',
author VARCHAR ( 200 ) DEFAULT NULL COMMENT 'Review writer',
a_id INT ( 20 ) DEFAULT NULL COMMENT 'Associated article ID'.PRIMARY KEY ( id )
) ENGINE = INNODB AUTO_INCREMENT = 3 DEFAULT CHARSET = utf8;
INSERT INTO t_comment VALUES ('1'.'Full and detailed'.'luccy'.'1');
INSERT INTO t_comment VALUES ('2'.'Like one'.'tom'.'1');
INSERT INTO t_comment VALUES ('3'.'Very detailed'.'eric'.'1');
INSERT INTO t_comment VALUES ('4'.'Very good. Very detailed.'.'Joe'.'1');
INSERT INTO t_comment VALUES ('5'.'Very good'.'bill'.'2');
Copy the code
2. Create projects and write features
You need to select web-Spring Web and SQL-mysql Driver
Add JPA Dependencies in SQL module, MySQL Dependencies, and Web Dependencies in Web module
(2) Compile entity classes corresponding to database tables and configure mapping relationships using JPA annotations
@Entity(name = "t_comment") // Set the ORM entity class and specify the table name for the mapping
public class Comment {
@Id // Indicates the primary key ID of the mapping
@GeneratedValue(strategy = GenerationType.IDENTITY) // Set the policy for increasing the primary key
private Integer id;
private String content;
private String author;
@Column(name = "a_id") // Specify the table field name for the mapping
private Integer aId;
// get/set/toString()
}
Copy the code
(3) Compile the Repository interface file for database operations
public interface CommentRepository extends JpaRepository<Comment.Integer> {
// Modify the comment author according to the comment ID
@Transactional
@Modifying
@Query(value = "update t_comment c set c.author = ? 1 where c.id=? 2",nativeQuery = true)
public int updateComment(String author,Integer id);
}
Copy the code
(4) Write the Service layer
For convenience, instead of writing the interface, write the Service class directly
@Service
public class CommentService {
@Autowired
private CommentRepository commentRepository;
public Comment findCommentById(Integer id) {
Optional<Comment> comment = commentRepository.findById(id);
if (comment.isPresent()) {
Comment comment1 = comment.get();
return comment1;
}
return null; }}Copy the code
(5) Write the Controller layer
@RestController
public class CommentController {
@Autowired
private CommentService commentService;
// http://localhost:8080/findCommentById? id=1
@RequestMapping(value = "/findCommentById")
public Comment findCommentById(Integer id) {
Comment comment = commentService.findCommentById(id);
returncomment; }}Copy the code
(6) Write configuration files
MySQL database connection configuration
spring.datasource.url=jdbc:mysql://localhost:3306/springbootdata? serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
# display SQL statements using JPA for database queries
spring.jpa.show-sql=true
# Enable camel name matching mapping
mybatis.configuration.map-underscore-to-camel-case=true
# Resolve garbled code
spring.http.encoding.force-response=true
Copy the code
(7) test
To access the url: http://localhost:8080/findCommentById? If id=1, the database will be queried on each access
Default Cache experience
(1) Enable annotation-based caching support using the @enablecaching annotation
@EnableCaching // Enable Spring Boot's annotation-based cache management support
@SpringBootApplication
public class Springboot05CacheApplication {
public static void main(String[] args) { SpringApplication.run(Springboot05CacheApplication.class, args); }}Copy the code
(2) Use the @cacheable annotation to manage caching of data manipulation methods.
// @cacheable: Store the comment of this method in the default springboot cache
//cacheNames: create a cache namespace with a unique cache identifier
@Cacheable(cacheNames = "comment")
public Comment findCommentById(Integer id) {
Optional<Comment> comment = commentRepository.findById(id);
if (comment.isPresent()) {
Comment comment1 = comment.get();
return comment1;
}
return null;
}
Copy the code
(3) Test access
At this point, the console will only query the database once after multiple visits
Underlying structure: In many automatic cache configuration class is SimpleCacheConfiguration SpringBoot default assembly, he USES a CacheManager is ConcurrentMapCacheManager, Use ConcurrentMap as the underlying data structure to query caches based on their names. Each Cache contains multiple k-V key-value pairs
(4) Introduction to cache annotations
@ EnableCaching annotations
@Enablecaching is provided by the Spring framework, and the SpringBoot framework inherits this annotation, which needs to be configured on the class (usually on the project startup class in Java) to enable annotation-based caching support
@ Cacheable annotations
The @cacheable annotation is also provided by the Spring framework and can be applied to classes or methods (typically data query methods).
The @cacheable annotation provides several attributes
The property name | instructions |
---|---|
value/cacheNames | Specifies the name of the cache space. This attribute is mandatory. Use either of these attributes |
key | Specifies the key to cache data. The default is the method parameter value. SpEL expressions can be used |
keyGenerator | Generator for the key that specifies the cache data, used either with the key property |
cacheManager | Specify the cache manager |
cacheResolver | Specifies the cache resolver, which can be used either with the cacheManager property |
condition | Specifies that data is cached if a condition is met |
unless | Specifies that data is not cached if a condition is met |
sync | Specifies whether to use asynchronous caching. The default false |
If the cacheNames method is used to query the Cache(Cache component), the Cache component is obtained by the name specified by cacheNames. If the Cache component is not obtained for the first time, the Cache component is automatically created.
If multiple or no parameters are generated according to a certain policy, the default is KeyGenerator. SimpleKeyGenerator is used to generate the key. SimpleKeyGenerator’s default strategy for generating keys:
Number of parameters | key |
---|---|
No parameters | new SimpleKey() |
There is one parameter | The parameter value |
Multiple parameters | new SimpleKey(params) |
@ CachePut annotations
The @cacheput annotation is used mostly for modification operations, even if the target value already exists in the cache, but it ensures that the method will still be executed and the result of the execution will be stored in the cache
@ CacheEvict annotations
The @cacheevict annotation is provided by the Spring framework and can be applied to classes or methods (typically data deletion methods) to delete cached data. The default execution order for @cacheevict annotations is to make method calls first and then clear the cache.
Second, integrate Redis cache implementation
Caching components supported by Spring Boot
In Spring Boot, Data storage is dependent on the Spring framework cache cache management related org. Springframework. Cache. The cache and org., springframework. Cache. CacheManager cache manager interface.
If the program does not define a Bean component of type CacheManager or a cacheResolver cache parser named cacheResolver, Spring Boot attempts to select and enable the following cache components (in the order specified):
(1) Generic
(2) JCache (JSR-107) (EhCache 3, Hazelcast, Infinispan, etc.) (3)EhCache 2.x
(4) Hazelcast
(5) Infinispan
(6) Couchbase
(7) Redis
(8) Caffeine
(9) Simple
Annotation-based Redis cache implementation
(1) Add Spring Data Redis dependent initiator.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Copy the code
(2)Redis service connection configuration
#Redis service address
spring.redis.host=127.0.0.1
#Redis server connection port
spring.redis.port=6379
Redis server connection password
spring.redis.password=
Copy the code
3. Modify methods in the CommentService class to customize cache management using @cacheable, @cacheput, and @cacheevict annotations to demonstrate cache storage, cache update, and cache deletion, respectively
@Service
public class CommentService {
@Autowired
private CommentRepository commentRepository;
// @cacheable: Store the comment of this method in the default springboot cache
//cacheNames: create a cache namespace with a unique cache identifier
// value: cache result key: default If there is only one parameter, the default key is the method parameter if there is no parameter or more than one parameter: simpleKeyGenerate
@Cacheable(cacheNames = "comment" ,unless = "#result == null")
public Comment findCommentById(Integer id) {
Optional<Comment> comment = commentRepository.findById(id);
if (comment.isPresent()) {
Comment comment1 = comment.get();
return comment1;
}
return null;
}
// Update the method
@CachePut(cacheNames = "comment",key = "#result.id")
public Comment updateComment(Comment comment){
commentRepository.updateComment(comment.getAuthor(),comment.getId());
return comment;
}
// Delete method
@CacheEvict(cacheNames = "comment")
public void deleteComment(Integer id){ commentRepository.deleteById(id); }}Copy the code
In the @cacheable annotation, “Unless = “#result==null” is defined to indicate that the query result is empty and will not be cached
(4) Serialize the cache object.
(5) Start the test and query the test
After the query, the cache value is stored in Redis
(6) Redis cache update test based on annotations.
http://localhost:8080/updateComment?id=1&author=aaabb, database changes
There are only update statements in log printing, not query statements.
FindById () : findById() : findById() : findById() : findById() : findById() : findById() : findById The @cachePUT cache update configuration is successful
(7) Annotation-based Redis cache deletion test
Visit: http://localhost:8080/deleteComment? Id =1 data is deleted, and the Redis cache is also deleted
In addition, you can set the cache validity period
# set a uniform validity period of 1 minute in milliseconds for annotated Redis cache data
spring.cache.redis.time-to-live=60000
Copy the code
In the above code, the “spring.cache.redis. Time-to-live” property is added to the Spring Boot global configuration file to uniformly set the validity period (in milliseconds) of redis data, but this approach is relatively flexible
Redis cache implementation based on API
(1) Use Redis API for business data cache management.
Modifying the Service method
@Service
public class ApiCommentService {
@Autowired
private CommentRepository commentRepository;
@Autowired
private RedisTemplate redisTemplate;
// Use the API to cache the database
public Comment findCommentById(Integer id) {
Object o = redisTemplate.opsForValue().get("comment_" + id);
if(o ! =null) {// Return if found in cache
return (Comment) o;
}
// If the cache does not exist, go to the database
Optional<Comment> comment = commentRepository.findById(id);
if (comment.isPresent()) {
Comment comment1 = comment.get();
// Save the query result to the cache. You can also set the validity period to 1 day
redisTemplate.opsForValue().set("comment_" + id,comment1,1, TimeUnit.DAYS);
return comment1;
}
return null;
}
/ / update
public Comment updateComment(Comment comment){
commentRepository.updateComment(comment.getAuthor(),comment.getId());
redisTemplate.opsForValue().set("comment_"+comment.getId(),comment);
return comment;
}
public void deleteComment(Integer id){
commentRepository.deleteById(id);
redisTemplate.delete("comment_"+id); }}Copy the code
(2) Write the Web access layer Controller file
@RestController
@RequestMapping("api")
public class ApiCommentController {
@Autowired
private ApiCommentService commentService;
// http://localhost:8080/api/findCommentById? id=1
@RequestMapping(value = "/findCommentById")
public Comment findCommentById(Integer id) {
Comment comment = commentService.findCommentById(id);
return comment;
}
@RequestMapping(value = "/updateComment")
public Comment updateComment(Comment comment) {
Comment commentById = commentService.findCommentById(comment.getId());
commentById.setAuthor(comment.getAuthor());
Comment res = commentService.updateComment(commentById);
return res;
}
@RequestMapping(value = "/deleteComment")
public void deleteComment(Integer id) { commentService.deleteComment(id); }}Copy the code
Api-based Redis cache implementation related configuration. The apI-based Redis caching implementation does not require the @Enablecaching annotation to enable annotation based caching support, so the @Enablecaching added to the project startup class can be deleted or commented out here
Custom cache serialization mechanism
Custom RedisTemplate
The Redis API default serialization mechanism
If you open the RedisTemplate class and look at the source information for the class, you can draw two conclusions:
(1) using RedisTemplate Redis data cache operation, internal default is JdkSerializationRedisSerializer serialization way, So entity classes that cache data must implement the JDK’s built-in serialization interfaces (such as Serializable);
(2) When RedisTemplate is used for Redis data cache operation, if defaultSerializer is customized, the customized serialization method will be used.
Each of the serialization types for cache data keys and values is RedisSerializer. The default implementation classes are as follows:
Custom RedisTemplate serialization mechanism
When you introduce Redis dependencies into your project, the RedisAutoConfiguration provided by Spring Boot automatically takes effect. Open the RedisAutoConfiguration class to see how RedisTemplate is defined in the internal source code
@Bean
@ConditionalOnMissingBean( name = {"redisTemplate"} )
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
Copy the code
In the Redis auto-configuration class, a RedisTemplate is initialized by the Redis connection factory RedisConnectionFactory. The @conditionalonmissingBean annotation (which, as the name implies, works when a Bean does not exist) has been added above the class to indicate that if the developer custom a Bean named redisTemplate, the default redisTemplate initialization will not take effect.
Customize a Bean component named redisTemplate along these lines
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
// Use JSON format to serialize objects and convert cached data keys and values
Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);
// Resolve the query cache conversion exception
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jacksonSeial.setObjectMapper(om);
/ / set API RedisTemplate template serialization to JSON template. SetDefaultSerializer (jacksonSeial);
returntemplate; }}Copy the code
The @Configuration annotation defines a RedisConfig Configuration class and uses the @Bean annotation to inject a redisTemplate component whose default name is the method name (note that the Bean component name must be redisTemplate). In the Bean of the definition of group a, the custom of a RedisTemplate, using custom Jackson2JsonRedisSerializer data serialization way; In the custom serialization mode, an ObjectMapper is defined for the data transformation Settings
The test results
Up and visit: http://localhost:8080/api/findCommentById? Id =3 The result is cached in Redis as follows:
Custom RedisCacheManager
For annotated templates, the following fixed templates are generally used:
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
// Create serialized objects in String format and JSON format respectively to convert cached data key and value
RedisSerializer<String> strSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jacksonSeial =
new Jackson2JsonRedisSerializer(Object.class);
// Resolve the query cache conversion exception
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jacksonSeial.setObjectMapper(om);
// Customize the cache data serialization method and duration
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofDays(1))
.serializeKeysWith(RedisSerializationContext.SerializationPair
.fromSerializer(strSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(jacksonSeial))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager
.builder(redisConnectionFactory).cacheDefaults(config).build();
return cacheManager;
}
Copy the code
Up and visit: http://localhost:8080/findCommentById? id=4