Spring Data: How to maintain cache consistency in transactional methods? - spring

Let's assume we have a standard Spring Boot application with JPA. We have repositories, services and REST controllers. On the service layer, we have this:
#Service
public class UserService {
#Autowired
private UserRepository userRepo;
#Transactional(readOnly = true)
public User getUserById(userId: String) {
return this.userRepo.findById(userId).orElse(null);
}
#Transactional(readOnly = false)
public User saveUser(User user){
this.userRepo.save(user);
}
}
We want to cache the result of getUserById, either via #Cacheable or via an explicit cache. We have the following two options:
If a call to saveUser(...) occurs, we call repo.save(user) and then we immediately put the saved user into our cache (write-through).
If a call to saveUser(...) occurs, we invalidate the corresponding entry in the cache. Once the user is requested by ID again, we read it from the database and put it into the cache.
Both methods have issues:
If we write-through, we risk that the database transaction fails at some point after the repo.save(user) call and is rolled back. We may have written a version of the user into our cache that never hit the database. Our cache is out-of-sync with the database.
If we only invalidate the cache, there is a time period between the invalidation and the transaction commit where a concurrent transaction may call getUserById(...). This transaction will still read the old version of the user and write it into the cache. The result is that we have outdated data in our cache.
Is the built-in spring cache susceptible to these issues as well? How do you avoid such problems in your application?

Ok so I got confused here. There is the Spring Boot side of caching, which is supposed to cache method results, and then there is the 2nd level cache of the JPA provider. These are different things with different purposes. My bad for mixing them up.

Related

how to Resolve "could not initialize proxy - no session" error when using Spring repository

I'm working on a mutitenant project it maintains different schema for each tenant, followed Project
As we are dynamically switching the tenants so it looks like some configuration is missed which is closing the session or not keeping the session open to fetch the LAZY loaded objects. Which results in "could not initialize proxy - no session" error.
Please check below link to access the complete project and db schema scripts, please follow the steps given in Readme file.
Project
It will be helpful if someone can point out the issue in the code.
i tried to put service methods in #Transactional annotation but that didn't work.
I'm expecting it to make another call to the LAZY loaded object, This project is simplefied verson of the complex project, actually i have lot more lazy loaded objects.
Issue:-
I'm getting no Session error "could not initialize proxy [com.amran.dynamic.multitenant.tenant.entity.Tenant#1] - no Session"
at line 26 (/dynamicmultitenant/src/main/java/com/amran/dynamic/multitenant/tenant/service/ProductServiceImpl.java)
The issue is that your transaction boundaries are not correct. In TenantDatabaseConfig and MasterDatabaseConfig you've correctly added #EnableTransactionManagement, which will setup transactions when requested.
However - the outermost component that has an (implicit) #Transactional annotation is the ProductRepository (by virtue of it being implemented by the SimpleJpaRepository class - which has the annotation applied to it - https://github.com/spring-projects/spring-data-jpa/blob/864c7c454dac61eb602674c4123d84e63f23d766/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java#L95 )
and so your productRepository.findAll(); call will start a transaction, create a JPA session, run the query, close the session, close the transaction, which means that there is no longer any transaction / session open in which to perform the lazy-loading.
Therefore, your original attempt of
i tried to put service methods in #Transactional annotation but that didn't work.
IS the correct thing to do.
You don't say exactly what you tried to do, and where, but there are a few things that could have gone wrong. Firstly, make sure you're adding a org.springframework.transaction.annotation.Transactional and not a javax.transaction.Transactional annotation.
Secondly (and the more likely problem in this scenario), you'll need to configure the annotation with which transaction manager the transaction should be bound to, otherwise it may use an existing / new transaction created against the master DB connection, not the tenant one.
In this case, I think that:
#Service
#Transactional(transactionManager = "tenantTransactionManager")
public class ProductServiceImpl implements ProductService {
should work for you, and make all the methods of the service be bound to a transaction on the tenant DB connection.
EDIT: Answering a follow-up question:
can you please also suggest a better way to inject my tenantTransactionManager in all my service classes, as I don't want to mention tenantTxnManger in all service classes if there is any better way to do it ?
Yes, sure. You can create a meta-annotation that applies multiple other annotations, so you could create:
/**
* Marks class as being a service operating on a single Tenant
*/
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Service
#Transactional("tenantTransactionManager")
public #interface TenantService {
}
and then you can simply annotate your service classes with #TenantService instead of #Service:
#TenantService
public class ProductServiceImpl implements ProductService {

Troubles with caching using SpringBoot

I'm currently having troubles with my redis cache configuration.
I was previously using a Redis CRUD repository with RedisHash objects. Everything was working fine.
I need to use #cacheable annotation for stuff which aren't linked to my crud repository.
So I had cache configuration with #EnableCaching annotation
#Bean
public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
return (builder) -> builder
.withCacheConfiguration("default",
RedisCacheConfiguration.defaultCacheConfig())
.withCacheConfiguration("ttlCache",
RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(10)));
}
Everything about my cache configuration is OK.
But now there is some trouble to store entities in the redis.
After searching it seems that my RedisHash object have to implements Serializable. Ok I do it.
But now all my methods in the repository are doing some strange thing (essentialy the get return empty). When i look in my redis repository i see some new items indicating that a cache is used for my repository.
My question is, is there a way to disable usage of cache for my repository ?
Thanks in advance.

Is it possible to manage the receipt of a Connection for a transaction in Spring (the mode Open Session in View)?

I use Spring boot to simplify working with the configuration.
When the RestController receives a request for it automatically (this is the default action), Connection is allocated from the datasource and until the transit is completed, Connection is open. (at least I have such information, you can correct it).
In is a mode: Open Session In View.
OSIV in Spring Boot is implemented using the OpenEntityManagerInViewInterceptor web request interceptor class. Unlike pure Spring, it is enabled here by default.
But before executing the transactional method, this request may be delayed on the business logic layer for processing, and the connection is "idle". This leads to Connection leaks.
It seems that you can control the output of connection when using Srpingboot .
But how is this done ?
Please give an example.
First. You should disable the mode Open Session in View.
spring.jpa.open-in-view=false
Then, you should use the #EntityGraph annotation.
public interface UserRepository extends CrudRepository<User, Long> {
#EntityGraph(attributePaths = "departments")
Optional<User> findDetailedByUsername(String username);
Optional<User> findByUsername(String username);
}
Beware of using Hibernate.initialize(), because it forms not one, but several queries to get related tables.

Redis cache context listener in spring boot how to know is redis cache up or not?

We have a spring application where redis cache has been implemented along with the database MySQL. Here we are using redis cache to store the temporary values for the server validations instead of hitting the database every time, hence hitting the database calls every time gets reduces system performance.
Now i explain my problem while hitting the spring boot action endpoints,
if suddenly my redis cache server stops, we would like to know how to get the notification that my redis cache server is down. So we need solution / example java application to get the notification using redis cache listener context or anything like that.
Redis doesn't work that way. In fact, no remote service will notify your application that it's down. Usually, it's the other way round: If the service you're consuming is accessed with a more or less sophisticated client, you might take advantage of the client's features.
Asynchronous clients that run I/O, or monitoring threads can help here. More specific, it depends on the client you're using with Spring Boot and Redis. Jedis is a plain client that reacts on a request basis. Lettuce allows you to register a RedisConnectionStateListener that is called on specific connection events, such as connected/disconnected:
RedisClient redisClient = …;
redisClient.addListener(new RedisConnectionStateListener() {
#Override
public void onRedisConnected(RedisChannelHandler<?, ?> redisChannelHandler) {
}
#Override
public void onRedisDisconnected(RedisChannelHandler<?, ?> redisChannelHandler) {
}
#Override
public void onRedisExceptionCaught(RedisChannelHandler<?, ?> redisChannelHandler, Throwable throwable) {
}
});
When using Spring Data Redis, retrieving the RedisClient from LettuceConnectionFactory might be a bit tricky as it is a private field. Hence it requires reflection.

Globally disable EntityManager cache in jboss

Is it possible to disable caching with EntityManager in some jboss config?
I'll explain. I have some final "ear" of our product that is using EntityManager through hibernate (something like this, i an newbie to this) and I need to test some behaviour. The easy way for me is to change(remove, create) state of entities direct in the database. But after i did this, the application remain to find old values for some time. I've read about some jboss cache, that is used for entity-manager.
So, for testing, i want to disable EntityManager cache, but it can not be disabled on application-level, only on jboss-level.
In brief: i need application always to reload actual entity state, because it can be edited in database with come other application. And its impossible to disable caching on application-level(hibernate.xml and other)
PS: jboss 4.2.3, ejb3, hibernate3
The cache you are referring to is probably the PersistenceContext. It cannot be disabled. You can only tweak it's scope. In a Java EE environment, the scope of the persistence context is the transaction per default. So if you need for some changes to take effect immediately, you can extract these changes (including fetching the entities in question) into a separate method and annotate it to require a new transaction:
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
Once the method returns, all changes are committed.
You could also use bean managed transactions, so you can control the commit yourself. For this, annotate your bean with #TransactionManagement( TransactionManagementType.BEAN ) and use UserTransaction:
#Resource
private UserTransaction tx;
...
tx.begin();
//do stuff
tx.commit();

Resources