I am using spring cache abstraction using Ehcache as a cache provider. I am trying to attach cache operations to spring JPA transactions, but not able to do so.
Even though transaction fails/rollback cache put happens.
Configuration,
@Bean
public EhCacheManagerFactoryBean cacheManagerUsingSpringApi() {
EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();
// provide xml file for ehcache configuration/
ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource("spring-cache-abs-ehcache.xml"));
return ehCacheManagerFactoryBean;
}
@Bean
public org.springframework.cache.CacheManager ehCacheCacheManager() {
final EhCacheCacheManager ehCacheCacheManager = new EhCacheCacheManager(cacheManagerUsingSpringApi().getObject());
ehCacheCacheManager.setTransactionAware(true); // Setting transaction aware
return ehCacheCacheManager;
}
spring-cache-abs-ehcache.xml,
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd"
updateCheck="true"
monitoring="autodetect"
dynamicConfig="true">
<cache name="EmployeeCache"
maxEntriesLocalHeap="10000"
eternal="false"
timeToIdleSeconds="300" timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LFU"
transactionalMode="off">
<persistence strategy="localTempSwap" />
</cache>
</ehcache>
EmployeeRepository,
public interface EmployeeRepository extends JpaRepository<Employee, Long>, CustomEmployeeRepository {
}
Transactional Method,
@Repository
public class EmployeeRepositoryImpl implements CustomEmployeeRepository {
@PersistenceContext
private EntityManager entityManager;
@Autowired
private CacheManager cacheManager;
// THIS METHOD SHOULD NOT PUT INTO CACHE WITH NEW NAME
@Override
@Transactional
@Cacheable(cacheNames = "EmployeeCache", key = "#a0.id")
public Employee customUpdate(Employee employee) {
employee.setFirstName(UUID.randomUUID().toString());
entityManager.merge(employee);
// rolling back transaction
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return employee;
}
}
Test case(caller),
@Test
public void testCustomUpdate() {
// GIVEN
Employee employee = new Employee();
employee.setFirstName(UUID.randomUUID().toString());
employee.setLastName(UUID.randomUUID().toString());
final Employee savedEmployee = employeeRepository.save(employee);
// WHEN
final Employee updatedEmployee = employeeRepository.customUpdate(savedEmployee);
// THEN
final Cache employeeCache = cacheManager.getCache("EmployeeCache");
final Cache.ValueWrapper object = employeeCache.get(updatedEmployee.getId());
assertNull(object);
}
Test Should succeed i.e spring should not PUT data into cache in method employeeRepository.customUpdate
if transaction was rollback in that method.
But, spring puts data into cache even if transactions fails.
NOTE: Weird part is, if entry already exists in cache , then @CachePut does not updates entry in cache if transaction fails.
So, if I annotate employeeRepository.save
with @CachePut(cacheNames = "EmployeeCache", key = "#result.id")
then cache is not updated in update call.
What is missing here?