SpringBoot advanced_ And cache_ one

1, JSR-107 specification
   Java Caching defines five core interfaces, namely cachengprovider, CacheManager, cache, entry and expiration.
   cacheingprovider defines the creation, configuration, acquisition, management and control of multiple cachemanagers. An application can access multiple cacheingproviders at runtime.
   CacheManager defines the creation, configuration, acquisition, management and control of multiple uniquely named caches, which exist in the context of CacheManager. A CacheManager is owned by only one cacheingprovider.
   Cache is a data structure similar to Map and temporarily stores values indexed by Key. A Cache is owned by only one CacheManager.
  Entry is a key value pair stored in the Cache.
   Expiry each entry stored in the cache has a defined validity period. Once this time is exceeded, the entry is expired. Once expired, entries will not be accessible, updated, or deleted. Cache validity can be set through ExpiryPolicy.


  there can be multiple caching providers in an application. One caching provider can obtain multiple cache managers. One cache manager manages different caches. In the cache, there are cache key value pairs (entries), and each entry has an expiration date. The relationship between cache manager and cache is somewhat similar to the relationship between connection pool and connection in database.
   using JSR-107 in development requires importing packages: generally, JSR-107 is not directly used for development, because JSR-107 only defines interfaces and does not implement them

<dependency>
    <groupId>javax.cache</groupId>
    <artifactId>cache-api</artifactId>
    <version>1.1.0</version>
</dependency>·

2, Spring cache abstraction
  1. Introduction
  Spring has defined org since 3.1 springframework. cache. Cache and
org.springframework.cache.CacheManager interface to unify different caching technologies; It also supports the use of JCache(JSR-107) annotations to simplify our development.
   the Cache interface is defined by the component specification of the Cache, including the collection of various operations (addition, deletion, modification and query) of the Cache. Under the Cache interface, Spring provides various implementations of xxcache, such as RedisCache, EhCacheCache, ConcurrentMapCache, etc. Every time a method requiring caching function is called, Spring will check whether the specified target method of the specified parameters has been called. If so, it will directly obtain the result of the method call from the Cache. If not, it will call the method and Cache the result and return it to the user. The next call is taken directly from the Cache.
  when using Spring cache abstraction, we need to pay attention to the following two points:
    ① determine the methods that need to be cached and their caching strategies
    ② read the data stored in the previous cache from the cache



explain:
    ① @ Cacheable is marked on the method, indicating that the result of the method needs to be cached. The cached key is determined by the keyGenerator policy, and the form of cached value is determined by the serialize serialization policy (serialization or json format); After the annotation is marked, when the method is called again within the cache time limit, the method itself will not be called, but the results will be obtained directly from the cache
    ② @ CachePut is also marked on the method. Similar to @ Cacheable, it also caches the return value of the method. The difference is that the method marked @ CachePut will be called every time, and the result will be cached every time, which is suitable for object update
  2. Basic environment construction
   ① create SpringBoot application: select Cache, Mysql, Mybatis and Web module, and pom is as follows

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.18.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.bdm</groupId>
    <artifactId>springboot-cache</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-cache</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

② Create database table

SET FOREIGN_KEY_CHECKS=0;

DROP TABLE IF EXISTS `department`;
CREATE TABLE `department` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `departmentName` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


DROP TABLE IF EXISTS `employee`;
CREATE TABLE `employee` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `lastName` varchar(255) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  `gender` int(2) DEFAULT NULL,
  `d_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

③ Create entity Bean corresponding to table

@Data
public class Employee {
    private Integer id;
    private String lastName;
    private String email;
    private Integer gender; //Gender 1 male 0 female
    private Integer dId;
}

@Data
public class Department {
    private Integer id;
    private String departmentName;
}

④ Integrate mybatis operation database
    data source configuration: the driver can not be written, and SpringBoot will automatically judge according to the connection

spring.datasource.url=jdbc:mysql://localhost:3306/springboot_cache
spring.datasource.username=root
spring.datasource.password=120288
#spring.datasource.driver-class-name=com.mysql.jdbc.Driver

#Open hump naming
mybatis.configuration.map-underscore-to-camel-case=true 

Use the annotated version of Mybatis: use @ MapperScan to specify the package where the mapper interface is located

@SpringBootApplication
@MapperScan(basePackages = "com.bdm.cache.mappers")
public class SpringbootCacheApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootCacheApplication.class, args);
    }
}

Create corresponding Mapper interface: annotate with @ Mapper annotation to indicate that it is a Mapper interface of mybatis

@Mapper
public interface EmployeeMapper {

    @Select("SELECT * FROM employee WHERE id = #{id}")
    public Employee getEmpById(Integer id);

    @Insert("INSERT INTO employee(lastName,email,gender,d_id) VALUES(#{lastName},#{email},#{gender},#{dId})")
    public void insertEmp(Employee employee);

    @Update("UPDATE employee SET lastName = #{lastName},email = #{email},gender = #{gender},d_id = #{dId} WHERE id = #{id}")
    public void updateEmp(Employee employee);

    @Delete("DELETE FROM employee WHERE id = #{id}")
    public void deleteEmpById(Integer id);
}

Write Service:

@Service
public class EmployeeService {

    @Autowired
    EmployeeMapper employeeMapper;

    public Employee getEmpById(Integer id){
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }
}

Write Controller:

@RestController
public class EmployeeController {

    @Autowired
    EmployeeService employeeService;

    @GetMapping("/emp/{id}")
    public Employee getEmp(@PathVariable("id") Integer id){
        return employeeService.getEmpById(id);
    }
}

test

3. @ Cacheable initial experience
  before testing, you can configure the Logger log to let the console print out Sql:

logging.level.com.bdm.cache.mappers=debug

① Enable annotation based caching function: Main startup class annotation @ EnableCaching

@SpringBootApplication
@MapperScan(basePackages = "com.bdm.cache.mappers")
@EnableCaching //Enable annotation based caching
public class SpringbootCacheApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootCacheApplication.class, args);
    }
}

② Annotations related to annotation cache: @ Cacheable, CacheEvict, CachePut
    a, @ Cacheable: cache the results of method operation. When you get the same data in the future, you can get it directly from the cache without calling the method

@Cacheable(cacheNames = {"emp"})
public Employee getEmpById(Integer id){
	Employee emp = employeeMapper.getEmpById(id);
	return emp;
}


Note:
   ① those that meet both conditions and unless conditions will not be cached
   ② when using asynchronous mode for caching (sync=true): the unless condition will not be supported
The available SpEL expressions are shown in the following table:


3, How cache works
  1. Automatic configuration class: CacheAutoConfiguration. The CacheConfigurationImportSelector imported through CacheAutoConfiguration will add some full class names of cached configuration classes to the array
  2. Cache configuration class   org springframework. boot. autoconfigure. cache. GenericCacheConfiguration
  org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration
  org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration
  org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration
  org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration
  org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration
  org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration
  org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration
  org.springframework.boot.autoconfigure.cache.GuavaCacheConfiguration
  org. springframework. boot. autoconfigure. cache. Simplecacheconfiguration (used by default)
  org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration
  3. Configuration class in effect by default: SimpleCacheConfiguration
  4. SimpleCacheConfiguration registers a CacheManager in the container: ConcurrentMapCacheManager

@Configuration
@ConditionalOnMissingBean({CacheManager.class})
@Conditional({CacheCondition.class})
class SimpleCacheConfiguration {
    private final CacheProperties cacheProperties;
    private final CacheManagerCustomizers customizerInvoker;

    SimpleCacheConfiguration(CacheProperties cacheProperties, CacheManagerCustomizers customizerInvoker) {
        this.cacheProperties = cacheProperties;
        this.customizerInvoker = customizerInvoker;
    }

    @Bean
    public ConcurrentMapCacheManager cacheManager() {
        ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
        List<String> cacheNames = this.cacheProperties.getCacheNames();
        if (!cacheNames.isEmpty()) {
            cacheManager.setCacheNames(cacheNames);
        }

        return (ConcurrentMapCacheManager)this.customizerInvoker.customize(cacheManager);
    }
}

5. The concurrent mapcachemanager allows you to obtain and create cache components of the concurrent mapcache type: the function of the concurrent mapcache is to save data in the concurrent map
  6. Operation process of @ Cacheable:
   ① before the method runs, first query the Cache (Cache component) and obtain it according to the name specified by cacheNames (CacheManager obtains the corresponding Cache first, and if there is no Cache component for the first time, it will be created automatically)
   ② go to the Cache to find the contents of the Cache. The key used is the parameter of the method by default:
    key is generated by keyGenerator by default, and SimpleKeyGenerator is used by default
    default policy for generating keys by SimpleKeyGenerator:
     if there are no parameters: key = new SimpleKey();
     if there is a parameter: key = parameter value
     if there are multiple parameters: key = new SimpleKey(params);
   ③ call the target method if the cache is not found
   ④ put the result returned by the target method into the cache
  summary: @ Cacheable marked method will check whether there is this data in the cache before execution. By default, query the cache according to the value of the parameter key. If not, run the method and put the result into the cache. When calling later, directly use the data in the cache.
  core:
  1 ️⃣ Use the CacheManager(ConcurrentMapCacheManager) to get the Cache(ConcurrentMapCache) component by name
  2 ️⃣ Keys are generated using keyGenerator, and SimpleKeyGenerator is used by default

4, Other properties of @ Cacheable
  1. cacheNames/value: Specifies the name of the cache component and the cache in which the returned result of the method is placed. The value is an array. Multiple caches can be specified
  2. Key: the key used for caching data. By default, the value of the method parameter is used. It can be calculated using the SpEL expression (e.g. key="#root.methodName + '[' + #id + ']'")
  3. keyGenerator: the generator of key. You can customize the generator of key. Choose one of key/keyGenerator
  user defined primary key generator: the KeyGenerator here is a cache related interface. Be careful not to import the wrong interface

@Configuration
public class MyCacheConfig {

    @Bean("myKeyGenerator")
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object o, Method method, Object... objects) {
                return method.getName() + "[" + Arrays.asList(objects) + "]";
            }
        };
    }
}

Use a custom KeyGenerator:

@Cacheable(cacheNames = {"emp"},keyGenerator = "myKeyGenerator",,condition = "#id > 1 and #id < 10")
public Employee getEmpById(Integer id){
	Employee emp = employeeMapper.getEmpById(id);
	return emp;
}

4. cacheManager: specify the cache manager or cacheResolver to specify the parser
5. condition: Specifies that the cache is only available if the conditions are met
6. Unless: negative caching. When the condition specified by unless is true, the return value of the method will not be cached, and the result can be obtained for judgment
7. sync: whether to use asynchronous mode caching

Added by sissy on Sun, 30 Jan 2022 05:12:52 +0200