Al leer repetidamente los mismos datos, surge la cuestión de la optimización, los datos no cambian o rara vez cambian, estos son varios libros de referencia y otra información, es decir La función de obtener datos por clave es determinista. Aquí, probablemente todos entiendan: ¡necesitamos un caché! ¿Por qué necesita realizar una búsqueda o cálculo de datos cada vez?
Así que aquí mostraré cómo hacer caché en Java Spring, y dado que está muy relacionado con la Base de Datos, cómo hacerlo en el DBMS usando un ejemplo específico.
Contenido
- Caché en primavera
- Caché en funciones Oracle PL-SQL
Caché en primavera
Luego, todo viene de la misma manera, en Java usan varios HasMap, ConcurrentMap, etc. En Spring también hay una solución para esto, simple, conveniente y efectiva. Creo que en la mayoría de los casos esto ayudará a resolver el problema. Y así, todo lo que se necesita es habilitar el caché y anotar la función.
Hacer accesible la caché
@SpringBootApplication @EnableCaching public class DemoCacheAbleApplication { public static void main(String[] args) { SpringApplication.run(DemoCacheAbleApplication.class, args); } }
Datos de búsqueda de la función de caché
@Cacheable(cacheNames="person") public Person findCacheByName(String name) {
La anotación indica el nombre de la memoria caché y hay otros parámetros. Funciona como se esperaba, la primera vez que se ejecuta el código, la clave coloca el resultado de la búsqueda en el caché (en este caso, el nombre) y las llamadas posteriores ya no se ejecutan, y los datos se recuperan del caché.
Un ejemplo de implementación del repositorio "Persona" usando un caché
@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; } }
Compruebo lo que pasó
@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(""); } }
En la prueba llamo dos veces
@Test public void findByName() { findCacheByName(""); findCacheByName(""); }
, la primera vez que se realiza una llamada, una búsqueda, la segunda vez que el resultado se toma del caché. Esto es visible en la consola.

Convenientemente, puede optimizar la funcionalidad existente de punta a punta. Si la función tiene más de un argumento, puede especificar el nombre del parámetro, que se utilizará como clave.
@Cacheable(cacheNames="person", key="#name") public Person findByKeyField(String name, Integer age) {
Existen esquemas más complejos para obtener una clave, esto se encuentra en la documentación.
Pero, por supuesto, la pregunta será cómo actualizar los datos en la memoria caché. Hay dos anotaciones para este propósito.
El primero es
@CachePut
Una función con esta anotación siempre llamará al código y colocará el resultado en el caché, para que pueda actualizar el caché.
Agregaré dos métodos al repositorio: eliminar y agregar Persona
public boolean delete(String name) { final Person person = findByName(name); return persons.remove(person); } public boolean add(Person person) { return persons.add(person); }
Haré una búsqueda de Persona, eliminaré, agregaré, buscaré nuevamente, pero como antes, obtendré la misma cara del caché hasta que llame "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; }
Prueba
@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(""); }

Otra anotación es
@CacheEvict
Le permite no solo visitar el almacenamiento en caché, sino también desalojar. Este proceso es útil para eliminar datos obsoletos o no utilizados de la memoria caché.
De forma predeterminada, Spring usa - ConcurrentMapCache para el caché, si tiene su propia clase excelente para organizar el caché, es posible especificar esto en el 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; } }
Los nombres de los cachés también se indican allí, puede haber varios. En la configuración xml, esto se indica así:
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>
Clase de persona 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; }
Estructura del proyecto

Aquí 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/> </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>
Caché en funciones Oracle PL-SQL
Bueno, al final, aquellos que no descuidan el poder del DBMS, pero lo usan, pueden usar el almacenamiento en caché a nivel de la base de datos, además o como alternativa. Por ejemplo, en Oracle, no es menos elegante convertir una función regular en una función con el almacenamiento en caché del resultado al agregarle
RESULT_CACHE
Un ejemplo:
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;
Después de cambiar los datos en la tabla, se reconstruirá el caché, puede ajustar la regla de caché usando
RELIES_ON (...)
Materiales
Abstracción de caché