how to stop default query on id when using #cacheable annotation - spring

When using #Cacheable annotation(org.springframework.cache.annotation.Cacheable) on the repository with custom key like name,getting the issue of running extra query on id field on every consecutive request.
See the repository code below:
public interface StatusRepository extends JpaRepository<Status, Integer> {
#Cacheable(value = "StatusByStatusNameCache" , key="#statusName")
public Status findByStatusName(String statusName);
}
Above you can see that a cache is defined for status name only, now after running first request got the following Hibernate console with query on status name:
Hibernate: select status0_.status_id as status_i1_7_, status0_.status_name as status_n2_7_ from status status0_ where status0_.status_name=?
Hibernate: select event0_.event_id as event_id1_3_, event0_.event_name as event_na2_3_ from events event0_ where event0_.event_name=?
now then hit another second request getting hibernate query in console with id :
Hibernate: select event_.event_id, event_.event_name as event_na2_3_ from events event_ where event_.event_id=?
Hibernate: select requestcha_.request_channel_id, requestcha_.request_channel_name as request_2_6_ from request_channels requestcha_ where requestcha_.request_channel_id=?
Hibernate: select status_.status_id, status_.status_name as status_n2_7_ from status status_ where status_.status_id=?
I don't understand why this extra query is firing as status_name query is cached but how to stop this id query on every consecutive call after first request.

The #Cacheable annotation by Spring is completely independent from the any cache provided by Hibernate/your JPA implementation.
The query by id will not be prevented by caching the result of the by name query, because the caching for the id query would be done by JPAs first level cache, which doesn't know or care about Springs cache.
Here is what is probably going on:
findbyName
entity is in 1st level cache and in Springs cache.
Any access by id (e.g. navigating to the entity)
entity gets served from 1st level cache.
session ends.
entity is removed from 1st level cache
findByName
entity is served from Springs cache. Note that this is now a detached entity. Nothing is in the 1st level cache.
access by id
entity is loaded from database, since it is not found in the 1st level cache.
You should enable Hibernates 2nd level cache to cache entities across sessions for access by id.
I also would advise against combining the caches of JPA/Hibernate with Spring Caches and rather use JPAs own query cache to cache findByName. See Spring JPA Hibernate query cache doesn't work for how to make it work with Spring Data JPA.
Also take a look at this article by Vlad Mihalcea about interaction of query cache and 2nd level cache.
Note that Oliver Drotbohm seems to have a different opinion.

Related

Spring JpaRepository Perform delete only if given Id exists and avoid race condition

my situtation is as follows:
I have #Entity class Ingredient in my Spring JPA Project.
I would like to implement a method performing delete operation on DB record by record Id
public boolean deleteIngredient(String id) and if possible avoid handling exceptions for non-existent Ids.
Unfortunately the only recommendations I can find in this area are based on the fact of querying by Id before deleting record e.g.
ingredientRepository.findById(id).ifPresent(x -> ingredientRepository.deleteById(id));
or
if(ingredientRepository.existsById(id)){
ingredientRepository.deleteById(id);
}
which I believe are prone to race conditions (other thread may delete record after this one queries for existence.
Is the best approach really just wrapping it in a try-catch block and handling EmptyResultDataAccessException in case record with given Id does not exist?
If you are using JPA, you need the entity to be marked for deletion in the persistence context (e.g. not in the Database). Keep in mind JPA Repository follows the ORM paradigm and is not acting on the record directly.
Any race conditions will be handled on the persistence context level.
If you use #Transactional and you will be safe.
Also if you don't want the explicit error thrown by deleteById, when the ID is not known to the EntityManager, consider using delete (which will just return with no exception being thrown in case the ID is unknown).

#cachePut for data update in list of object in spring boot

Hi I am using Spring Boot Cache in my application. I am able to fetch data from db and cached that data.
#Cacheable("employee")
public Optional<List<Employee>> employeeData(){
log.info("Fetched employee details from DB and cached in memory!!");
return employeeRepository.findActiveEmployee();
}
I want to delete or update or add new record in cached object.
How can I use #cachePut to update existing record or insert record or delete existing record based on some condition.
You can keep your original method as is to call it whenever you want to fetch data from cache. Note that the #Cacheable annotation does not execute the method's body if the cache with name "employee" is not empty, instead it returns the results from cache.
#Cacheable("employee")
public Optional<List<Employee>> employeeData(){
}
Then proceed in creating a new method annotated with #CachePut. Having in mind that #CachePut annotation will both execute the method as well as cache the results each and every time:
#CachePut(value="employee", condition="#name=='Tom'")
public Optional<List<Employee>> employeeDataCacheByName(String name){
log.info("Fetched employee details from DB and cached in memory depending on condition!!");
return employeeRepository.findActiveEmployee();
}
The above method will run the query every time and put into employee cache the results if the name argument is "Tom" (condition logic is up to you, this is just an example). This way cache is always updated with the results from the database (as long as the condition is truly evaluated).
For deleting (I don't think #CachePut can be used) maybe you can combine the #CacheEvict annotation, you can see an example in this Answer: https://stackoverflow.com/a/62488344/3635454

Cache and Improve performance of JpaRepository method

I have the below code.I am using SPRING BOOT and JAVA 8
#Repository
public interface LogRepository extends JpaRepository<Log, Integer>{
List<Log> findByDate( Date date) ;
findByDate is returning me suppose 1 million records.findByDate( Date date) method gets fired whenever user is hitting the url /api/get/logs.
How can I keep the data in cache ?I don't want to hit the database every time method findByDate( Date date) is called.
Step 1: You need to add #EnableCaching annotation with #SpringBootApplication in your main class.
Step 2: Add #Cacheable annotation in your method like below
#Cacheable(value="user", key="#date")
User findByDate( Date date) ;
For more details regarding the different cache configuration, you can follow be https://howtodoinjava.com/spring-boot2/spring-boot-cache-example/
Here a great guide about caching and eviction in spring boot https://www.baeldung.com/spring-cache-tutorial.
However, you shouldn’t cache 1000000 records from a single query. It’s too expensive and you may risk a socketTimeoutException or a 502 timeout from client/application server. You should implement pagination on your Services (already provided by JPA itself) and eventually cache the pages (pay attention to the updates - take also a look at cache merging annotations)

Spring framework. How to store some data to prevent each time access db

I have in my application list of customers and users. I would like get the list of them only on start. Then use data that is stored locally, not from DB.
Can You advice me some methods with examples?
I think about HttpSession object? But I am not sure is it ok?
Cause this data should be available only for logged user, that access it on start.
List of customers will be available on each page off application!
Take a look at Spring cache http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html
You can annotate your repository methods:
#Cacheable(value="customer", key="#prsonalNum")
public Customer findCustomer(String prsonalNum) {
...
}
The application will enter the method body only the first time. After that the value will be taken from the cache.
You can also evict the cache when you update some customer for example.
#CacheEvict(value="customer", allEntries=true)
public void addCustomer(Customer cust)
For synchronizing insert and update operations with the cache use #CachePut annotation and for synchronizing delete operations with the cache use #CacheEvict annotation.
Use the same cache name (value paramter) and same key value
And you should enable caching with #EnableCaching annotation on one of your configuration classes.
You can still use the HttpSession object, however, only put it in the session once, the user has logged in... and you can remove it from the session on page close...
If you want to reuse the data multiple times and do not want to query each time. i would suggested to create a simple Cache suing HashMap or HashTable.
You can easily save the list of customers across every id in such data structure.
and in spring you can easily create a singleton bean which will hold this hashmap and accessible across the application.

Doctrine - Break query cache when entity is modified (second-level cache)

I'm using doctrine 2 without caching anything at the moment. I'd like to enable some caching system within Doctrine but it looks like you have to manage it manually everywhere:
$memcache = new Memcache();
$memcache->connect('memcache_host', 11211);
$cacheDriver = new \Doctrine\Common\Cache\MemcacheCache();
$cacheDriver->setMemcache($memcache);
$cacheDriver->save('cache_id', 'my_data');
...
$cacheDriver->delete('cache_id');
I'd like to know if Doctrine could manage this automatically. For instance:
The cache is enable, I request a User entity by id, Doctrine search in its cache, cannot find the user, fetch it, set it into the cache, return it.
I fetch a second time, Doctrine return me the cached User.
I update the User (or any of its relations) Doctrine detect it and break the cache for this object
I request the same User by id, Doctrine doesn't have it in cache anymore, fetch it and set the cache back with the updated user before to return it
Is that possible?
Cheers,
Maxime
What you are looking for (in Doctrine ORM) is only supported in the resultset cache, and only applies to results of SQL queries produced by DQL queries.
The exact name for the feature you are looking for is "second-level cache", which is not yet supported by Doctrine ORM, but is currently being developed (will hopefully be available in version 2.5) at https://github.com/doctrine/doctrine2/pull/580
For now, you will have to handle this kind of caching in your own service layer if it is really needed.
If you are pulling the entity by it's primary key, the caching will be done by the doctrine's "identity map" as described here http://doctrine-orm.readthedocs.org/en/latest/reference/unitofwork.html

Resources