4

I'd like to add a @CacheEvict() decorator to my save / saveAll methods from my repository.

I've tried to override the methods by using custom implementation by reading this thread but I don't want to rewrite the method implementation, just call the default one with more behavior. None of the solutions provided work well for my case, or require far too much customization.

@Transactional(readOnly = true)
@Repository
public interface MyRepository extends JpaRepository<MyEntity, Long> {

   // This is working fine as its a custom query method
   @Cacheable(value = "ENTITIES")
   MyEntity findByCategory(String category);

   // This isn't working as it won't implement the function correctly. Save don't work anymore here.
   @CacheEvict(value = "ENTITIES", key = "#entity.hashCode()")
   <S extends MyEntity> S save(S entity);

}

I could call the cache eviction manually from the method's body, but I can't just override the save() method and call the super.save() since I only use interfaces and let Spring generate the implementation.

Any advices?

Kapcash
  • 6,377
  • 2
  • 18
  • 40
  • I think it should be returning in the save method. it should be working fine – Na Felix Wimpy Wijaya Jun 25 '19 at 08:54
  • Nice try but sorry, that was just a mistake pasting my sample code! – Kapcash Jun 25 '19 at 08:56
  • Is your problem is the "save" isn't working (data is not saving) or the cache is not evicted correctly? – Na Felix Wimpy Wijaya Jun 25 '19 at 08:58
  • Don't... Please don't instead use proper 2nd level caching of your persistence provider. – M. Deinum Jun 25 '19 at 09:02
  • Why don't you pass and return MyEntity to the save method. And second HashCode is not a good idea for the key and are you sure that findByCategory uses this as key as well? And you have no write transaction anymore. You have to add @Transactional to save. – Simon Martinelli Jun 25 '19 at 09:09
  • Could you apply an AOP based solution? https://stackoverflow.com/questions/26258158/how-to-instrument-advice-a-spring-data-jpa-repository – Alan Hay Jun 25 '19 at 09:19
  • The real question here is "how do I add such a decorator to the default repository methods?" Don't mind the use of the cache, I know the 2nd level cache, and I know the hashCode isn't the best solution. – Kapcash Jun 25 '19 at 09:22
  • An aspect (AOP) which interacts with the CacheManager programmatically and uses reflection to determine name of the cache to clear should be a clean solution: https://www.baeldung.com/spring-boot-evict-cache – Alan Hay Jun 25 '19 at 09:29
  • 1
    there's official example from spring data https://github.com/spring-projects/spring-data-examples/blob/master/jpa/example/src/main/java/example/springdata/jpa/caching/CachingUserRepository.java it's exactly the same with what you wrote so it should be working (the save) if the cacheevict not working means you specified the wrong key – Na Felix Wimpy Wijaya Jun 25 '19 at 09:33
  • @NaFelixWimpyWijaya Yes I feel like this solution should be working, but I can't manage how. The save method doesn't rise any exception but still, don't save anything as expected... – Kapcash Jun 25 '19 at 09:59

1 Answers1

2

Found. It was dummy. Just removed the @Transactional(readOnly = true). It was preventing the save from working.

So just overriding the signature of the method is enough to add some decorators without caring about the implementation.

@Override
@CacheEvict(value = "ENTITIES", key = "#p0.getKeyWhateverItIs()")
<S extends Feature> S save(S entity);

And yes, the key value was wrong as well, but not implicated in the non-working save method.

Kapcash
  • 6,377
  • 2
  • 18
  • 40