用Spring @Transactional缓存本地事务



我正在尝试设置事务性ehcache,利用Spring @Cacheable和@Transactional。

我的缓存与@Cacheable工作良好,但一旦我设置我的缓存使用本地事务:

<cache name="currencyCodeMaps" maxElementsInMemory="100" overflowToDisk="false" timeToIdleSeconds="5" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU" transactionalMode="local"/>

当我访问缓存时,我得到错误:

net.sf.ehcache.transaction.TransactionException: transaction not started

,即使相同的方法被注释为@Transactional。我的Spring事务管理器是:

org.springframework.orm.jpa.JpaTransactionManager

ehcache文档说本地事务是显式控制的:

本地事务不受事务管理器控制。相反,有一个显式API,在该API中获取对CacheManager使用的TransactionControllergettransactioncontroller()和事务被显式调用

但是这将是困难的,因为我想同步我的ehcache事务与DB事务,而DB事务是由@Transactional控制的。

是否有办法让本地Ehcache事务与Spring @Transactional一起工作?

是的,有一种方法可以实现你的目标。

  1. 因为你有2个事务资源(JTA和Ehcache),不使用JTA,你必须使用复合事务管理器,如org.springframework.data.transaction.ChainedTransactionManager从spring-data项目

    @Bean
    public PlatformTransactionManager transactionManager() {
        return new ChainedTransactionManager(ehcacheTransactionManager(), jpaTransactionManager());
    }
    @Bean
    public EhcacheTransactionManager ehcacheTransactionManager() {
        return new EhcacheTransactionManager(ehcacheManager().getTransactionController());
    }
    @Bean
    public PlatformTransactionManager jpaTransactionManager() {
        return new JpaTransactionManager(entityManagerFactory());
    }
    
  2. 您需要指定默认情况下应该使用哪个事务管理器:

    @Configuration
    public class Configuration implements TransactionManagementConfigurer {
    ...
        @Override
        public PlatformTransactionManager annotationDrivenTransactionManager() {
            return transactionManager();
        }
    ...
    }
    
  3. EhcacheTransactionManager实现
    import net.sf.ehcache.TransactionController;
    import net.sf.ehcache.transaction.local.LocalTransactionContext;
    import org.springframework.transaction.TransactionDefinition;
    import org.springframework.transaction.TransactionException;
    import org.springframework.transaction.support.AbstractPlatformTransactionManager;
    import org.springframework.transaction.support.DefaultTransactionStatus;
        public class EhcacheTransactionManager extends AbstractPlatformTransactionManager {
        private TransactionController transactionController;
        public EhcacheTransactionManager(TransactionController transactionController) {
            this.transactionController = transactionController;
        }
        @Override
        protected Object doGetTransaction() throws TransactionException {
            return new EhcacheTransactionObject(transactionController.getCurrentTransactionContext());
        }
        @Override
        protected void doBegin(Object o, TransactionDefinition transactionDefinition) throws TransactionException {
            int timeout = transactionDefinition.getTimeout();
            if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
                transactionController.begin(timeout);
            } else {
                transactionController.begin();
            }
        }
        @Override
        protected void doCommit(DefaultTransactionStatus defaultTransactionStatus) throws TransactionException {
            transactionController.commit();
        }
        @Override
        protected void doRollback(DefaultTransactionStatus defaultTransactionStatus) throws TransactionException {
            transactionController.rollback();
        }
        public class EhcacheTransactionObject {
            private LocalTransactionContext currentTransactionContext;
            public EhcacheTransactionObject(LocalTransactionContext currentTransactionContext) {
                this.currentTransactionContext = currentTransactionContext;
            }
        }
    }
    

源代码和测试用例可以在这里找到

这个解决方案有一个明显的缺点ehcache的事务协调器不支持挂起/恢复操作,所以内部事务(PROPAGATION_REQUIRES_NEW)是不可能的。这就是为什么我必须另找一个。

另一种选择是根本不使用本地ehcache事务,而是使用org.springframework.cache.transaction.AbstractTransactionSupportingCacheManager#setTransactionAware来装饰缓存以延迟操作,直到事务结束。但它有以下缺点:

  1. 被驱逐的键在事务内保持可访问,直到事务提交
  2. putIfAbsent操作未延迟

这对我来说是个问题,所以我用不同的方式实现了这个功能。检查"me.qnox.springframework.cache.tx。TxAwareCacheManagerProxy',上面描述的问题已经解决,在同一个存储库

您不需要本地事务,您需要XA事务,这是Ehcache支持的。

查看Ehcache 2.10的文档。

最新更新