How to get non cached result from a cacheable method? - spring

Let's say a business method annotated with #Cacheable.
#Component
class Some {
#Cacheable
public Some getSome() {
}
}
Now can any caller conditionally request non cached result, as if the method is not annotated with #Cacheable at all, without evicting the cache nor using a separated method?
void doOther() {
// How can I get non-cached live result from some#getSome()
}
#Autowired
private Some some;
I found condition and unless, but those are not for my requirements.

Caching mechanism through #Cacheable is achieved by using proxies. To ignore caching you should unwrap proxy into real target object, and directly invoke its unwrapped method:
import org.springframework.aop.framework.AopProxyUtils;
...
#Autowired
private Some some;
void doOther() {
Some unproxied = (Some) AopProxyUtils.getSingletonTarget(some);
Some noncached = unproxied.getSome();
...
}

Related

#Cacheable testing over method

I have a #Cacheable method inside a class.
I try to create that cache after a first call to that method, then, the second call should't go inside the method getCacheLeads.
#Service
public class LeadService {
#Autowired
private LeadRepository leadRepository;
#Autowired
public LeadService(LeadRepository leadRepository) {
this.leadRepository = leadRepository;
}
public void calculateLead(Lead leadBean) {
Lead lead = this.getCacheLeads(leadBean);
}
#Cacheable(cacheNames="leads", key="#leadBean.leadId")
public Lead getCacheLeads(Lead leadBean){
Lead result = leadRepository.findByLeadId(leadBean.getLeadId());
***logic to transform de Lead object***
return result;
}
}
But during testing that cache is never used, calling it twice with same parameter (serviceIsCalled) to ensure it is called twice to check it.
#ExtendWith(SpringExtension.class)
public class LeadServiceTest {
private LeadService leadService;
#Mock
private LeadRepository leadRepository;
#Autowired
CacheManager cacheManager;
#BeforeEach
public void setUp(){
leadService = new LeadService(leadRepository);
}
#Configuration
#EnableCaching
static class Config {
#Bean
CacheManager cacheManager() {
return new ConcurrentMapCacheManager("leads");
}
}
#Test
public void testLead(){
givenData();
serviceIsCalled();
serviceIsCalled();
checkDataArray();
}
private void givenData() {
Lead lead = new Lead();
lead.setLeadId("DC635EA19A39EA128764BB99052E5D1A9A");
Mockito.when(leadRepository.findByLeadId(any()))
.thenReturn(lead);
}
private void serviceIsCalled(){
Lead lead = new Lead();
lead.setLeadId("DC635EA19A39EA128764BB99052E5D1A9A");
leadService.calculateLead(lead);
}
private void checkDataArray(){
verify(leadRepository, times(1)).findByLeadId(anyString());
}
}
Why is it called 2 times?
You have a lot of things going on here, and someone looking at this and answering your question would definitely have to read between the lines.
First, your Spring configuration is not even correct. You are declaring the names of all the caches used by your Spring application (and tests) "statically" with the use of the ConcurrentMapCacheManager constructor accepting an array of cache names as the argument.
NOTE: Caches identified explicitly by name, and only these caches, are available at runtime.
#Bean
CacheManager cacheManager() {
return new ConcurrentMapCacheManager("LEAD_DATA");
}
In this case, your 1 and only cache is called "LEAD_DATA".
NOTE: Only the no arg constructor in `ConcurrentMapCacheManager allows dynamically created caches by name at runtime.
But then, in your #Service LeadService class, #Cacheable getCacheLeads(:Lead) method, you declare the cache to use as "leads".
#Service
public class LeadService {
#Cacheable(cacheNames="leads", key="#leadBean.leadId")
public Lead getCacheLeads(Lead leadBean){
// ...
}
}
This miss configuration will actually lead to an Exception at runtime similar to the following:
java.lang.IllegalArgumentException: Cannot find cache named 'leads' for Builder[public io.stackoverflow.questions.spring.cache.StaticCacheNamesIntegrationTests$Lead io.stackoverflow.questions.spring.cache.StaticCacheNamesIntegrationTests$LeadService.load(io.stackoverflow.questions.spring.cache.StaticCacheNamesIntegrationTests$Lead)] caches=[leads] | key='#lead.id' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'
at org.springframework.cache.interceptor.AbstractCacheResolver.resolveCaches(AbstractCacheResolver.java:92)
at org.springframework.cache.interceptor.CacheAspectSupport.getCaches(CacheAspectSupport.java:252)
at org.springframework.cache.interceptor.CacheAspectSupport$CacheOperationContext.<init>(CacheAspectSupport.java:724)
at org.springframework.cache.interceptor.CacheAspectSupport.getOperationContext(CacheAspectSupport.java:265)
at org.springframework.cache.interceptor.CacheAspectSupport$CacheOperationContexts.<init>(CacheAspectSupport.java:615)
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:345)
at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:64)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:698)
at io.stackoverflow.questions.spring.cache.StaticCacheNamesIntegrationTests$LeadService$$EnhancerBySpringCGLIB$$86664246.load(<generated>)
...
..
.
Additionally, I don't see anything "outside" of the LeadsService bean calling the #Cacheable, getCacheLeads(..) method. Inside your test, you are calling:
leadService.calculateLead(lead);
As follows:
private void serviceIsCalled(){
Lead lead = new Lead();
lead.setLeadId("DC635EA19A39EA128764BB99052E5D1A9A");
leadService.calculateLead(lead);
}
If the calculateLead(:Lead) LeadService method is calling the #Cacheable, getCacheLeads(:Lead) LeadService method (internally), then that is not going to cause the caching functionality to kick in since you are already "behind" the AOP Proxy setup by Spring to "enable" caching behavior for your LeadService bean.
See the Spring Framework AOP documentation on this matter.
NOTE: Spring's Cache Abstraction, like the Spring's Transaction Management, is built on the Spring AOP infrastructure, as are many other things in Spring.
In your case this means:
Test -> <PROXY> -> LeadService.calculateLead(:Lead) -> LeadService.getCacheLeads(:Lead)
However, between LeadSevice.calculateLead(:Lead) and LeadService.getCacheLeads(:Lead), NO PROXY is involved, therefore Spring's caching behavior will not be applied.
Only...
Test (or some other bean) -> <PROXY> -> LeadService.getCacheLeads(:Lead)
Will result in the AOP Proxy decorated with the Caching Interceptors being invoked and the caching behavior applied.
You can see that your use case will work correctly when configured and used correctly as demonstrated in my example test class, modeled after your domain.
Look for the comments that explain why your configuration will fail in your case.

Does Transactional.TxType.REQUIRES_NEW start a new transaction when called from the same bean?

AFAIK
In proxy mode (which is the default), only 'external' method calls
coming in through the proxy will be intercepted. This means that
'self-invocation', i.e. a method within the target object calling some
other method of the target object, won't lead to an actual transaction
at runtime even if the invoked method is marked with #Transactional!
But here is: Transactional.TxType.REQUIRES_NEW
Will be the second transaction created?
#Service
public class SomeService {
#Transactional
public void doSomeLogic() {
// some logic
doOtherThings();
// some logic
}
#Transactional(Transactional.TxType.REQUIRES_NEW)
private void doOtherThings() {
// some logic
}
To get an answer to this question, you need to know how a proxy works.
When you annotate a method inside a bean, the proxy will wrap that bean with the appropriate logic. This means that if you call that annotated method of your bean, the request will first be sent to the proxy object (named with $), which will then call the bean's method. If this method calls another method of the same bean, it will call it without invoking a proxy which has a logic, e.x., of transaction management.
Example: Here is the code which will be wrapped by proxy and an appropriate diagram of its work.
#Service
public class SomeService {
#Transactional
public void foo() {
// this next method invocation is a direct call on the 'this' reference
bar();
}
#Transactional(Transactional.TxType.REQUIRES_NEW)
public void bar() {
// some logic...
}
}
Source
Hence, the answer is No.
Hope that's a little bit more clear now.

Spring Constraint Validation Context - Database Request Caching

I've written a custom Validation Annotation and a ConstraintValidator implementation, which uses a Spring Service (and executes a Database Query):
public class MyValidator implements ConstraintValidator<MyValidationAnnotation, String> {
private final MyService service;
public MyValidator(MyService service) {
this.service = service;
}
#Override
public void initialize(MyValidationAnnotation constraintAnnotation) {}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return service.exists(value);
}
}
It's used like this:
public class MyEntity {
#Valid
List<Foo> list;
}
public class Foo {
#MyValidationAnnotation
String id;
}
This works quite nice, but service.exists(value) is getting called for every item within the list, which is correct, but could/should be optimized.
Question:
When validating an instance of MyEntity, I'd like to cache the results of the service.exists(value) calls.
I don't want to use a static HashMap<String, Boolean>, because this would cache the results for the entire application lifetime.
Is it possible to access some kind of Constraint Validation Context, which only exists while this particular validation is running, so I can put there the cached results?
Or do you have some other solution?
Thanks in advance!
You can use Spring's cache support. There might be other parts in the application which needs caching and this can be reused. And the setup is very simple too. And it will keep your code neat and readable.
You can cache your service calls. You need to put annotation on your service methods and a little bit of configuration.
And for cache provider you can use Ehcache. You have many options like setting ttl and max number of elements that can be cached and eviction policy etc etc if needed.
Or you can implement your own cache provider if your needs are simple. And if it is web request, In this cache you may find ThreadLocal to be useful. you can do all caching for this running thread using threadlocal. When the request is processed you can clear the threadlocal cache.

Spring nested transactions

In my Spring Boot project I have implemented following service method:
#Transactional
public boolean validateBoard(Board board) {
boolean result = false;
if (inProgress(board)) {
if (!canPlayWithCurrentBoard(board)) {
update(board, new Date(), Board.AFK);
throw new InvalidStateException(ErrorMessage.BOARD_TIMEOUT_REACHED);
}
if (!canSelectCards(board)) {
update(board, new Date(), Board.COMPLETED);
throw new InvalidStateException(ErrorMessage.ALL_BOARD_CARDS_ALREADY_SELECTED);
}
result = true;
}
return result;
}
Inside this method I use another service method which is called update:
#Transactional(propagation = Propagation.REQUIRES_NEW)
public Board update(Board board, Date finishedDate, Integer status) {
board.setStatus(status);
board.setFinishedDate(finishedDate);
return boardRepository.save(board);
}
I need to commit changes to database in update method independently of the owner transaction which is started in validateBoard method. Right now any changes is rolling back in case of any exception.
Even with #Transactional(propagation = Propagation.REQUIRES_NEW) it doesn't work.
How to correctly do this with Spring and allow nested transactions ?
This documentation covers your problem - https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/data-access.html#transaction-declarative-annotations
In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with #Transactional. Also, the proxy must be fully initialized to provide the expected behaviour so you should not rely on this feature in your initialization code, i.e. #PostConstruct.
However, there is an option to switch to AspectJ mode
Using "self" inject pattern you can resolve this issue.
sample code like below:
#Service #Transactional
public class YourService {
//... your member
#Autowired
private YourService self; //inject proxy as an instance member variable ;
#Transactional(propagation= Propagation.REQUIRES_NEW)
public void methodFoo() {
//...
}
public void methodBar() {
//call self.methodFoo() rather than this.methodFoo()
self.methodFoo();
}
}
The point is using "self" rather than "this".
The basic thumb rule in terms of nested Transactions is that they are completely dependent on the underlying database, i.e. support for Nested Transactions and their handling is database dependent and varies with it.
In some databases, changes made by the nested transaction are not seen by the 'host' transaction until the nested transaction is committed. This can be achieved using Transaction isolation in #Transactional (isolation = "")
You need to identify the place in your code from where an exception is thrown, i.e. from the parent method: "validateBoard" or from the child method: "update".
Your code snippet shows that you are explicitly throwing the exceptions.
YOU MUST KNOW::
In its default configuration, Spring Framework’s transaction
infrastructure code only marks a transaction for rollback in the case
of runtime, unchecked exceptions; that is when the thrown exception is
an instance or subclass of RuntimeException.
But #Transactional never rolls back a transaction for any checked exception.
Thus, Spring allows you to define
Exception for which transaction should be rollbacked
Exception for which transaction shouldn't be rollbacked
Try annotating your child method: update with #Transactional(no-rollback-for="ExceptionName") or your parent method.
Your transaction annotation in update method will not be regarded by Spring transaction infrastructure if called from some method of same class. For more understanding on how Spring transaction infrastructure works please refer to this.
Your problem is a method's call from another method inside the same proxy.It's self-invocation.
In your case, you can easily fix it without moving a method inside another service (why do you need to create another service just for moving some method from one service to another just for avoid self-invocation?), just to call the second method not directly from current class, but from spring container. In this case you call proxy second method with transaction not with self-invocatio.
This principle is useful for any proxy-object when you need self-invocation, not only a transactional proxy.
#Service
class SomeService ..... {
-->> #Autorired
-->> private ApplicationContext context;
-->> //or with implementing ApplicationContextAware
#Transactional(any propagation , it's not important in this case)
public boolean methodOne(SomeObject object) {
.......
-->> here you get a proxy from context and call a method from this proxy
-->>context.getBean(SomeService.class).
methodTwo(object);
......
}
#Transactional(any propagation , it's not important in this case)public boolean
methodTwo(SomeObject object) {
.......
}
}
when you do call context.getBean(SomeService.class).methodTwo(object); container returns proxy object and on this proxy you can call methodTwo(...) with transaction.
You could create a new service (CustomTransactionalService) that will run your code in a new transaction :
#Service
public class CustomTransactionalService {
#Transactional(propagation= Propagation.REQUIRES_NEW)
public <U> U runInNewTransaction(final Supplier<U> supplier) {
return supplier.get();
}
#Transactional(propagation= Propagation.REQUIRES_NEW)
public void runInNewTransaction(final Runnable runnable) {
runnable.run();
}
}
And then :
#Service
public class YourService {
#Autowired
private CustomTransactionalService customTransactionalService;
#Transactional
public boolean validateBoard(Board board) {
// ...
}
public Board update(Board board, Date finishedDate, Integer status) {
this.customTransactionalService.runInNewTransaction(() -> {
// ...
});
}
}

How to test Spring's declarative caching support on Spring Data repositories?

I have developed a Spring Data repository, MemberRepository interface, that extends org.springframework.data.jpa.repository.JpaRepository. MemberRepository has a method:
#Cacheable(CacheConfiguration.DATABASE_CACHE_NAME)
Member findByEmail(String email);
The result is cached by Spring cache abstraction (backed by a ConcurrentMapCache).
The issue I have is that I want to write an integration test (against hsqldb) that asserts that the result is retrieved from db the first time and from cache the second time.
I initially thought of mocking the jpa infrastructure (entity manager, etc.) and somehow assert that the entity manager is not called the second time but it seems too hard/cumbersome (see https://stackoverflow.com/a/23442457/536299).
Can someone then please provide advice as to how to test the caching behavior of a Spring Data Repository method annotated with #Cacheable?
If you want to test a technical aspect like caching, don't use a database at all. It's important to understand what you'd like to test here. You want to make sure the method invocation is avoided for the invocation with the very same arguments. The repository fronting a database is a completely orthogonal aspect to this topic.
Here's what I'd recommend:
Set up an integration test that configures declarative caching (or imports the necessary bit's and pieces from your production configuration.
Configure a mock instance of your repository.
Write a test case to set up the expected behavior of the mock, invoke the methods and verify the output accordingly.
Sample
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class CachingIntegrationTest {
// Your repository interface
interface MyRepo extends Repository<Object, Long> {
#Cacheable("sample")
Object findByEmail(String email);
}
#Configuration
#EnableCaching
static class Config {
// Simulating your caching configuration
#Bean
CacheManager cacheManager() {
return new ConcurrentMapCacheManager("sample");
}
// A repository mock instead of the real proxy
#Bean
MyRepo myRepo() {
return Mockito.mock(MyRepo.class);
}
}
#Autowired CacheManager manager;
#Autowired MyRepo repo;
#Test
public void methodInvocationShouldBeCached() {
Object first = new Object();
Object second = new Object();
// Set up the mock to return *different* objects for the first and second call
Mockito.when(repo.findByEmail(Mockito.any(String.class))).thenReturn(first, second);
// First invocation returns object returned by the method
Object result = repo.findByEmail("foo");
assertThat(result, is(first));
// Second invocation should return cached value, *not* second (as set up above)
result = repo.findByEmail("foo");
assertThat(result, is(first));
// Verify repository method was invoked once
Mockito.verify(repo, Mockito.times(1)).findByEmail("foo");
assertThat(manager.getCache("sample").get("foo"), is(notNullValue()));
// Third invocation with different key is triggers the second invocation of the repo method
result = repo.findByEmail("bar");
assertThat(result, is(second));
}
}
As you can see, we do a bit of over-testing here:
The most relevant check, I think is that the second call returns the first object. That's what the caching is all about. The first two calls with the same key return the same object, whereas the third call with a different key results in the second actual invocation on the repository.
We strengthen the test case by checking that the cache actually has a value for the first key. One could even extend that to check for the actual value. On the other hand, I also think it's fine to avoid doing that as you tend to test more of the internals of the mechanism rather than the application level behavior.
Key take-aways
You don't need any infrastructure to be in place to test container behavior.
Setting a test case up is easy and straight forward.
Well-designed components let you write simple test cases and require less integration leg work for testing.
I tried testing the cache behavior in my app using Oliver's example. In my case my cache is set at the service layer and I want to verify that my repo is being called the right number of times. I'm using spock mocks instead of mockito. I spent some time trying to figure out why my tests are failing, until I realized that tests running first are populating the cache and effecting the other tests. After clearing the cache for every test they started behaving as expected.
Here's what I ended up with:
#ContextConfiguration
class FooBarServiceCacheTest extends Specification {
#TestConfiguration
#EnableCaching
static class Config {
def mockFactory = new DetachedMockFactory()
def fooBarRepository = mockFactory.Mock(FooBarRepository)
#Bean
CacheManager cacheManager() {
new ConcurrentMapCacheManager(FOOBARS)
}
#Bean
FooBarRepository fooBarRepository() {
fooBarRepository
}
#Bean
FooBarService getFooBarService() {
new FooBarService(fooBarRepository)
}
}
#Autowired
#Subject
FooBarService fooBarService
#Autowired
FooBarRepository fooBarRepository
#Autowired
CacheManager cacheManager
def "setup"(){
// we want to start each test with an new cache
cacheManager.getCache(FOOBARS).clear()
}
def "should return cached foobars "() {
given:
final foobars = [new FooBar(), new FooBar()]
when:
fooBarService.getFooBars()
fooBarService.getFooBars()
final fooBars = fooBarService.getFooBars()
then:
1 * fooBarRepository.findAll() >> foobars
}
def "should return new foobars after clearing cache"() {
given:
final foobars = [new FooBar(), new FooBar()]
when:
fooBarService.getFooBars()
fooBarService.clearCache()
final fooBars = fooBarService.getFooBars()
then:
2 * fooBarRepository.findAll() >> foobars
}
}

Resources