Spring Transaction Aware 缓存不起作用

Posted

技术标签:

【中文标题】Spring Transaction Aware 缓存不起作用【英文标题】:Spring Transaction Aware cache is not working 【发布时间】:2020-02-08 13:35:45 【问题描述】:

我正在使用 Ehcache 作为缓存提供程序来使用 Spring 缓存抽象。 我正在尝试将缓存操作附加到 Spring JPA 事务,但无法这样做。

即使事务失败/回滚缓存放置发生。

配置,

        @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>

员工存储库,

    public interface EmployeeRepository extends JpaRepository<Employee, Long>, CustomEmployeeRepository 
    

事务方法,

    @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
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);

测试应该成功,即如果事务在该方法中回滚,则 spring 不应将数据放入方法 employeeRepository.customUpdate 中的缓存中。 但是,即使事务失败,spring 也会将数据放入缓存中。 注意:奇怪的是,如果缓存中已经存在条目,那么如果事务失败,@CachePut 不会更新缓存中的条目。

所以,如果我用@CachePut(cacheNames = "EmployeeCache", key = "#result.id") 注释employeeRepository.save,那么缓存不会在更新调用中更新。

这里缺少什么?

【问题讨论】:

【参考方案1】:

选项 1:使用建议排序,

参考: https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#aop-ordering

下面的声明将导致事务通知首先被执行,然后是可缓存的通知。

<tx:annotation-driven order="0"/>

<cache:annotation-driven cache-manager="ehCacheManager" order="1"/>

选项 2:在不同的方法上注释 @Cacheable 和 @Transactional

如果您从另一个使用@Transactional 注释的方法调用@Cacheable 注释方法,您将看到代码正常工作。除非您实施了建议排序,否则同一方法上的两个注释(即 @Cacheable 和 @Transaction)都不会帮助您解决问题。请在下面查看您的逻辑的粗略实现。

    @Override
    @Transactional
    public Employee customUpdate(Employee employee) 
        Employee mergedEmployee = updateHelper(employee);

        // rolling back transaction 
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

        return mergedEmployee;
    

    @Cacheable(cacheNames = "EmployeeCache", key = "#a0.id")
    public Employee updateHelper(Employee employee)) 
        employee.setFirstName(UUID.randomUUID().toString());
        //On a merge(...) call on the entityManager, the returned item is the managed 
        //instance
        return entityManager.merge(employee); 
    


【讨论】:

以上是关于Spring Transaction Aware 缓存不起作用的主要内容,如果未能解决你的问题,请参考以下文章

Spring的感知能力 Aware

spring中aware接口的

Spring Aware接口详解

011 aware

002 aware专题

Spring Aware原理