📜 ⬆️ ⬇️

Data Caching - Java Spring

Repeating the same data many times, the question of optimization arises, the data does not change or rarely change, these are different reference books and other information, i.e. the function of obtaining data by key is deterministic. There probably everyone understands - you need a cache! Why do you need to re-search for data or calculation every time?

So here I will show how to do a cache in Java Spring and since it is closely connected most likely with the Database, then how to do this in a DBMS using the example of one particular.

Content
')

Cache in Spring


Then they all come in approximately the same way, in Java they use various HasMap, ConcurrentMap, etc. In Spring, too, for this there is a solution, simple, convenient, effective. I think that in most cases this will help in solving the problem. And so, all you need is to turn on the cache and annotate the function.

Making the cache available

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

Cache data search function

  @Cacheable(cacheNames="person") public Person findCacheByName(String name) { //... } 

The annotation indicates the name of the cache and there are other parameters. It works as expected, the first time the code is executed, the search result is placed in the cache by key (in this case, the name) and subsequent calls the code is no longer executed, and the data is retrieved from the cache.

An example of the implementation of the repository "Person" using the cache

 @Component public class PersonRepository { private static final Logger logger = LoggerFactory.getLogger(PersonRepository.class); private List<Person> persons = new ArrayList<>(); public void initPersons(List<Person> persons) { this.persons.addAll(persons); } private Person findByName(String name) { Person person = persons.stream() .filter(p -> p.getName().equals(name)) .findFirst() .orElse(null); return person; } @Cacheable(cacheNames="person") public Person findCacheByName(String name) { logger.info("find person ... " + name); final Person person = findByName(name); return person; } } 

Checking what happened

 @RunWith(SpringRunner.class) @SpringBootTest public class DemoCacheAbleApplicationTests { private static final Logger logger = LoggerFactory.getLogger(DemoCacheAbleApplicationTests.class); @Autowired private PersonRepository personRepository; @Before public void before() { personRepository.initPersons(Arrays.asList(new Person("", 22), new Person("", 34), new Person("", 41))); } private Person findCacheByName(String name) { logger.info("begin find " + name); final Person person = personRepository.findCacheByName(name); logger.info("find result = " + person.toString()); return person; } @Test public void findByName() { findCacheByName(""); findCacheByName(""); } } 

In the test I call twice

 @Test public void findByName() { findCacheByName(""); findCacheByName(""); } 

The first time a call is made, the search is made, the second time the result is taken from the cache This can be seen in the console.

image

Conveniently, you can point-optimize existing functionality. If the function has more than one argument, you can specify the name of the parameter, which one to use as a key.

  @Cacheable(cacheNames="person", key="#name") public Person findByKeyField(String name, Integer age) { 

There are more complex schemes for obtaining a key, this is in the documentation.

But of course the question will be how to update the data in the cache? For this purpose there are two annotations.

The first is @CachePut

A function with this annotation will always call the code, and place the result in the cache, so that it can update the cache.

I will add two methods to the repository: deleting and adding Person

  public boolean delete(String name) { final Person person = findByName(name); return persons.remove(person); } public boolean add(Person person) { return persons.add(person); } 

Perform a Person search, delete, add, search again, but still will get the same person from the cache until I call “findByNameAndPut”

  @CachePut(cacheNames="person") public Person findByNameAndPut(String name) { logger.info("findByName and put person ... " + name); final Person person = findByName(name); logger.info("put in cache person " + person); return person; } 

Test

  @Test public void findCacheByNameAndPut() { Person person = findCacheByName(""); logger.info("delete " + person); personRepository.delete(""); findCacheByName(""); logger.info("add new person"); person = new Person("", 35); personRepository.add(person); findCacheByName(""); logger.info("put new"); personRepository.findByNameAndPut(""); findCacheByName(""); } 

image

Another annotation is @CacheEvict

Allows you not only to visit the cache storage, but also to evict. This process is useful for removing obsolete or unused data from the cache.

By default, Spring uses for the cache - ConcurrentMapCache, if there is a different class for organizing the cache, it is possible to specify this in the CacheManager

 @SpringBootApplication @EnableCaching public class DemoCacheAbleApplication { public static void main(String[] args) { SpringApplication.run(DemoCacheAbleApplication.class, args); } @Bean public CacheManager cacheManager() { SimpleCacheManager cacheManager = new SimpleCacheManager(); cacheManager.setCaches(Arrays.asList( new ConcurrentMapCache("person"), new ConcurrentMapCache("addresses"))); return cacheManager; } } 

The names of caches are also indicated there, there may be several of them. In the xml configuration, this is indicated as follows:

Spring configuration.xml
 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"> <cache:annotation-driven/> <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager"> <property name="caches"> <set> <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" name="person"/> <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" name="addresses"/> </set> </property> </bean> </beans> 


Person class
 public class Person { private String name; private Integer age; public Person(String name, Integer age) { this.name = name; this.age = age; } public String getName() { return name; } public Integer getAge() { return age; } @Override public String toString() { return name + ":" + age; } 


Project structure

image

Here pom.xml
 <?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> <groupId>com.example</groupId> <artifactId>demoCacheAble</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>DemoCacheAble</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <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-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> 


Cache in Oracle PL-SQL functions


Well, in the end, those who do not neglect the power of the DBMS, but use it, can use caching at the database level, in addition or as an alternative. So, for example, in Oracle, it is no less elegant to turn an ordinary function into a function with caching the result, adding to it
RESULT_CACHE

Example:

 CREATE OR REPLACE FUNCTION GET_COUNTRY_NAME(P_CODE IN VARCHAR2) RETURN VARCHAR2 RESULT_CACHE IS CODE_RESULT VARCHAR2(50); BEGIN SELECT COUNTRY_NAME INTO CODE_RESULT FROM COUNTRIES WHERE COUNTRY_ID = P_CODE; --    dbms_lock.sleep (1); RETURN(CODE_RESULT); END; 

After changing the data in the table, the cache will be rebuilt, you can fine-tune the cache rule with
RELIES_ON (...)
Materials
Cache abstraction

Source: https://habr.com/ru/post/427187/


All Articles