问题
We are using Spring JPA in our project without any L2 cache. As per the docs, within a session, the related entities are returned from the L1 cache.
To test this out, in application.yml I enabled the query generation
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
In the implementation class, I added the following two lines of code.
Employee emp1 = employeeRepository.getByEmployeeCode(empCode);
Employee emp2 = employeeRepository.getByEmployeeCode(empCode);
I was hoping to see the query printed only once. But to my surprise, the query was printed twice on the console.
Then I assumed that the query is just generated multiple times irrespective of from where the entity is picked. But in this article about L2 cache, it is mentioned that the query is generated only once if the entity is present in the L2 cache. And I quote
You could also enable logging of SQL generated by Hibernate and invoke fooService.findOne(foo.getId()) multiple times in the test to verify that the select statement for loading Foo is printed only once (the first time), meaning that in subsequent calls the entity instance is fetched from the cache.
I am confused and I would like to the reason for this behavior? TIA.
回答1:
By default an entity will be retrieved from the first level cache only when you try to obtain the entity by id.
Example:
// first time the entity will be retrieved from DB
// under the hood it calls EntityManager.find method
Optional<Employee> employee1 = employeeRepository.findById(1L);
// here the entity will be retrieved from L1
Optional<Employee> employee2 = employeeRepository.findById(1L);
When you call the method:
Employee emp2 = employeeRepository.getByEmployeeCode(empCode);
actually spring data jpa generates and runs some HQL for you, but HQL is not cached by default. So you should do some additional things.
You can use @Cacheable annotation for caching your employeeRepository.getByEmployeeCode
method:
import org.springframework.cache.annotation.Cacheable;
public interface EmployeeRepository extends CrudRepository<Employee, Long>
{
@Cacheable("employeeByCode")
List<Employee> getByEmployeeCode(String name);
}
This is also assume that you have configured cache manager. Below you can see simple example:
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.cache.support.SimpleCacheManager;
@Configuration
@EnableCaching
public class CachingConfig
{
@Bean
public CacheManager cacheManager()
{
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("employeeByCode")));
return cacheManager;
}
}
To prevent usage of outdated data you should remember about cache eviction.
来源:https://stackoverflow.com/questions/65756768/entity-not-returned-from-l1-cache