简体   繁体   中英

Spring boot @Cacheable not working as expected with @Transactional

I am not able to share the actual code because of corporate policies but below is an example of method structures.

So in the example I want to the cache on the method in Class B to be cleared when the exception is thrown in class A.

NB: I can not move the cache to Class A so that is not a feasible solution.

I have tried reading all answers and posts online to get this working but not able to figure it out.

Please help with suggestions. A

I have set the following properties in application.properties

spring.cache.enabled=true
spring.cache.jcache.config=classpath:cache/ehcache.xml

@EnableCaching
@EnableTransactionManagement
    Main Class{


@Autowired
CacheManager cacheManager

@PostConstruct
void postConstruct(){
(JCacheCacheManager)cachemanager).setTransactionAware(true);

}
}

@Service
Class A{

@Autowired
B b;

@Transactional
public List<Data> getAllBusinessData(){

List<Data> dataList = b.getDataFromSystem("key");

//TestCode to test cache clears if exception thrown here
throw new RuntimeException("test");

}
}

@Service
Class B{

@Cacheable("cacheName")
public List<Data> getDataFromSystem(String key){

client call code here

return dataList;

}

}

There should be other ways, but the following could be a valid solution.

The first step will be to define a custom exception in order to be able to handle it later as appropriate. This exception will receive, among others, the name of the cache and the key you want to evict. For example:

public class CauseOfEvictionException extends RuntimeException {

  public CauseOfEvictionException(String message, String cacheName, String cacheKey) {
    super(message);
    
    this.cacheName = cacheName;
    this.cacheKey = cacheKey;
  }

  // getters and setters omitted for brevity
}

This exception will be raised by your B class, in your example:

@Service
Class A{

  @Autowired
  B b;

  @Transactional
  public List<Data> getAllBusinessData(){
 
    List<Data> dataList = b.getDataFromSystem("key");

    // Sorry, because in a certain sense you need to be aware of the cache
    // name here. Probably it could be improved
    throw new CauseOfEvictionException("test", "cacheName", "key");

  }
}

Now, we need a way to handle this kind of exception.

Independently of that way, the idea is that the code responsible for handling the exception will access the configured CacheManager and trigger the cache eviction.

Because you are using Spring Boot, an easy way to deal with it is by extending ResponseEntityExceptionHandler to provide an appropriate @ExceptionHandler . Please, consider read for more information the answer I provided in this related SO question orthis great article .

In summary, please, consider for example:

@ControllerAdvice
public class CustomExceptionHandler extends ResponseEntityExceptionHandler {
  
  @Autowired
  private CacheManager cacheManager;

  @ExceptionHandler(CauseOfEvictionException.class)
  public ResponseEntity<Object> handleCauseOfEvictionException(
    CauseOfEvictionException e) {
    this.cacheManager.getCache(e.getCacheName()).evict(e.getCacheKey());

    // handle the exception and provide the necessary response as you wish
    return ...;
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM