With the accumulation of time, the number of users of the application is increasing, and the data scale is also increasing. Often, database query operations will become the bottleneck that affects the user experience. At this time, caching is often a very good way to solve this problem. Spring 3 starts with powerful annotation-based caching support that can be used to add caching capabilities to legacy Spring applications in a less intrusive manner and improve data access performance.

The support for caching in Spring Boot provides a series of automated configurations that make it very easy to use caching. Let’s use a simple example to show how we can add caching to an existing application.

Quick start

We will build on the example of accessing MySQL using Spring Data JPA. This example includes access to User Data using Spring Data JPA. Using this foundation, we add a cache to reduce IO to the database for faster access. If you are not familiar with the implementation of MySQL read and write operations, it is recommended to read the previous article to complete the basic case writing.

Let’s briefly review the basics of this case:

Definition of User entity

@Entity @Data @NoArgsConstructor public class User { @Id @GeneratedValue private Long id; private String name; private Integer age; public User(String name, Integer age) { this.name = name; this.age = age; }}Copy the code

Data access implementation of User entity

public interface UserRepository extends JpaRepository<User, Long> {

    User findByName(String name);

    User findByNameAndAge(String name, Integer age);

    @Query("from User u where u.name=:name")
    User findUser(@Param("name") String name);

}Copy the code

To better understand caching, let’s make some simple changes to the project.

  • application.propertiesAdd to filespring.jpa.show-sql=trueTo enable Hibernate to print SQL statements. If the version is 1.x, usespring.jpa.properties.hibernate.show_sql=trueParameters.
  • Modify the unit test class and insert into the User table a row with User name AAA and age 10. The findByName function was used to complete two queries. The specific code is as follows:
@RunWith(SpringRunner.class) @SpringBootTest public class Chapter51ApplicationTests { @Autowired private UserRepository userRepository; @test public void Test () throws Exception {// Create 1 record userRepository.save(new User("AAA", 10)); User u1 = userRepository.findByName("AAA"); System.out.println(" first query: "+ u1.getage ()); User u2 = userRepository.findByName("AAA"); System.out.println(" second query: "+ u2.getage ()); }}Copy the code

Before adding the cache, we can execute the case and see the following log:

Hibernate: Id as id1_0_, user0_. Age as age2_0_, user0_. Name as name3_0_ from user user0_ where user0_. 10 Hibernate: Id as id1_0_, user0_. Age as age2_0_, user0_. Name as name3_0_ from user user0_ where user0_Copy the code

Both findByName queries execute two SQL queries against the MySQL database.

The introduction of the cache

Step 1: Introduce a cache dependency in pom.xml by adding the following:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>Copy the code

Step 2: add the @enablecaching annotation to the Spring Boot main class to EnableCaching as follows:

@EnableCaching @SpringBootApplication public class Chapter51Application { public static void main(String[] args) { SpringApplication.run(Chapter51Application.class, args); }}Copy the code

Step 3: Add cache configuration annotations to the data access interface, such as:

@CacheConfig(cacheNames = "users")
public interface UserRepository extends JpaRepository<User, Long> {

    @Cacheable
    User findByName(String name);

}Copy the code

Step 4: Execute the following unit tests again, and output the following from the console

Hibernate: insert into user (age, name, id) values (? ,? ,?) Hibernate: Id as id1_0_, user0_. Age as age2_0_, user0_. Name as name3_0_ from user user0_ where user0_. 10 Second query: 10Copy the code

At this point, we can see that when the findByName function is called the second time, the select statement is not executed again, thus directly reducing a database read.

To get a better view of the cache’s storage, we can inject CacheManager into our unit tests.

@Autowired
private CacheManager cacheManager;Copy the code

Run unit tests in debug mode to observe the cache set Users in CacheManager and the cache of the User object in it.

As you can see, CacheManager saves the query result after the first call to the findByName function, so on the second visit, it matches without accessing the database.

Details about Cache configuration notes

Let’s go back and see what the two annotations used here do:

  • @CacheConfig: used to configure common cache configurations that will be used in this class. Here,@CacheConfig(cacheNames = "users"): configure the content returned from the data access object to be stored in the cache object named Users, or we can pass it directly without using this annotation@CacheableConfigure the name of the cache set to define.
  • @CacheableThe return value of the findByName function configured will be added to the cache. At the same time, during the query, the database will be obtained from the cache first. If the database does not exist, the access to the database will be initiated. This annotation takes the following parameters:
    • value,cacheNames: two equivalent parameters (cacheNamesAdded for Spring 4 asvalueIs used to specify the collection name of the cache store. Due to new additions in Spring 4@CacheConfig, so it was a must in Spring 3valueProperties are also not required
    • keyBy default, all parameters of the function are used as the key value. If you configure the value yourself, you need to use the SpEL expression, for example:@Cacheable(key = "#p0"): Uses the first parameter of the function as the cache key. See SpEL expressions for more detailsThe official documentation
    • conditionSpEL expressions are used only if the expression conditions are met. For example:@Cacheable(key = "#p0", condition = "#p0.length() < 3")Is cached only when the length of the first parameter is less than 3. If this configuration is performed, AAA users above will not be cached.
    • unless: Another cache condition parameter, which is not required and uses the SpEL expression. It is different fromconditionThe argument is judged by its timing. This condition is judged after the function is called, so it can be judged by result.
    • keyGenerator: Specifies the key generator. It is not required. If we need to specify a custom key generator, we need to implement itorg.springframework.cache.interceptor.KeyGeneratorInterface and specify it with this parameter. Note that:The parameters andkeyAre mutually exclusive
    • cacheManager: Specifies which cache manager to use. It is not required. This parameter is required only when there are multiple hosts
    • cacheResolver: Specifies which cache parser to use. It is not required. To be byorg.springframework.cache.interceptor.CacheResolverInterface to implement its own cache parser, specified with this parameter.

In addition to the two annotations used here, there are several core annotations:

  • @CachePut: is configured on a function that can be cached based on parameters that define conditions@CacheableThe difference is that it actually calls the function every time, so it is mainly used for data addition and modification operations. Its parameters and@CacheableSimilar, the specific function can refer to the above@CacheableParsing of parameters
  • @CacheEvict: configured on a function, usually used in the delete method to remove data from the cache. In addition to@CacheableIn addition to the same parameters, it takes the following two parameters:
    • allEntries: Optional, false by default. When true, all data is removed
    • beforeInvocation: Optional, defaults to false and removes data after the method is called. When true, the data is removed before the method is called.

Code sample

For an example of this article, see the chapter5-1 directory in the repository below:

  • Github:github.com/dyc87112/Sp…
  • Gitee:gitee.com/didispace/S…

If you found this article good, welcomeStarSupport, your attention is my motivation!

Spring Boot 2. X basic tutorial: Use of in-process caching and Cache notes in detail. Welcome to pay attention to my official account: Program ape DD, for exclusive learning resources and daily dry goods push. If you are interested in my other topics, direct them to my personal blog: didispace.com.