Cache de Dados - Java Spring

Lendo repetidamente os mesmos dados, surge a questão da otimização, os dados não mudam ou raramente mudam; esses são vários livros de referência e outras informações, ou seja, a função de obter dados por chave é determinística. Aqui, provavelmente todo mundo entende - precisamos de um cache! Por que você precisa realizar uma pesquisa ou cálculo de dados toda vez?

Então, aqui mostrarei como fazer cache no Java Spring e, como é mais provável que esteja relacionado ao banco de dados, como fazê-lo no DBMS usando um exemplo específico.

Conteúdo

  • Cache na primavera
  • Cache nas funções Oracle PL-SQL

Cache na primavera


Em seguida, tudo ocorre aproximadamente da mesma maneira. Em Java, eles usam vários HasMap, ConcurrentMap, etc. No Spring também há uma solução para isso, simples, conveniente e eficaz. Penso que, na maioria dos casos, isso ajudará a resolver o problema. E assim, tudo o que é necessário é habilitar o cache e anotar a função.

Tornando o cache acessível

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

Dados de pesquisa da função de cache

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

A anotação indica o nome do cache e há outros parâmetros. Funciona como esperado, na primeira vez em que o código é executado, o resultado da pesquisa é colocado no cache pela chave (neste nome de caso) e as chamadas subseqüentes não são mais executadas e os dados são recuperados do cache.

Um exemplo de implementação do repositório "Person" usando um 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; } } 

Eu verifico o que aconteceu

 @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(""); } } 

No teste eu ligo duas vezes

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

, a primeira vez que uma chamada, uma pesquisa e a segunda vez que o resultado é obtido do cache. Isso é visível no console.

imagem

Convenientemente, você pode otimizar a funcionalidade existente no sentido do ponto. Se a função tiver mais de um argumento, você poderá especificar o nome do parâmetro, que será usado como chave.

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

Existem esquemas mais complexos para obter uma chave, isso está na documentação.

Mas é claro que a questão será como atualizar os dados no cache? Existem duas anotações para esse fim.

O primeiro é @CachePut

Uma função com esta anotação sempre chama o código e coloca o resultado no cache, para que ele possa atualizar o cache.

Vou adicionar dois métodos ao repositório: excluir e adicionar Person

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

Farei uma pesquisa por Pessoa, excluir, adicionar, pesquisar novamente, mas como antes, receberei a mesma face do cache até chamar "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; } 

Teste

  @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(""); } 

imagem

Outra anotação é @CacheEvict

Permite que você não apenas visite o armazenamento em cache, mas também despeje. Esse processo é útil para remover dados obsoletos ou não utilizados do cache.

Por padrão, o Spring usa - ConcurrentMapCache para o cache, se você tiver sua própria classe excelente para organizar o cache, é possível especificar isso no 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; } } 

Os nomes dos caches também são indicados lá, pode haver vários. Na configuração xml, isso é indicado assim:

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> 


Classe de pessoa
 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; } 


Estrutura do projeto

imagem

Aqui 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 nas funções Oracle PL-SQL


Bem, no final, aqueles que não negligenciam o poder do DBMS, mas o usam, podem usar o cache no nível do banco de dados, além ou como alternativa. Por exemplo, no Oracle, você não pode transformar de maneira menos elegante uma função comum em uma função com armazenamento em cache do resultado adicionando
RESULT_CACHE

Um exemplo:

 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; 

Depois de alterar os dados na tabela, o cache será reconstruído, você poderá ajustar a regra de cache usando
RELIES_ON (...)
Materiais
Abstração de cache

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


All Articles