Ehcache与Spring @Transactional进行本地交易

时间:2015-08-11 14:12:52

标签: spring transactions ehcache

我正在尝试设置事务性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 documentation表示本地交易是明确控制的:

  

本地交易不受交易管理器控制。   相反,有一个显式的API,其中获取引用   使用CacheManager的TransactionController   cacheManager.getTransactionController()和中的步骤   事务被明确调用

但这很难,因为我想将我的ehcache事务与数据库事务同步,而数据库事务由@Transactional控制。

有没有办法让本地Ehcache交易与Spring @Transactional一起使用?

2 个答案:

答案 0 :(得分:3)

是的,有办法实现目标。

  1. 因为您有2个事务资源(JTA和Ehcache)并且不使用JTA,所以您必须使用spring-data项目中的org.springframework.data.transaction.ChainedTransactionManager之类的复合事务管理器

    @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;
            }
    
        }
    
    }
    
  4. 可以找到源代码和测试用例here

    此解决方案具有显着缺点,ehcache的事务协调器不支持挂起/恢复操作,因此无法进行内部事务(PROPAGATION_REQUIRES_NEW)。这就是为什么我必须找到另一个。

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

    1. 在交易提交之前,被驱逐的密钥在交易中保持可访问状态
    2. putIfAbsent操作未被推迟
    3. 这对我来说是一个问题,所以我以不同的方式实现了这个功能。检查'me.qnox.springframework.cache.tx.TxAwareCacheManagerProxy',上面描述的问题解决了,在同一个存储库中

答案 1 :(得分:0)

您不希望本地交易,您想要Ehcache支持的XA交易。

查看Ehcache 2.10.xEhcache 2.8.x的文档。