【问题标题】:Ehcache local transactions with Spring @Transactional使用 Spring @Transactional 进行 Ehcache 本地事务
【发布时间】:2015-11-03 19:44:08
【问题描述】:

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

我的缓存与@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 事务与 DB 事务同步,而 DB 事务由 @Transactional 控制。

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

【问题讨论】:

    标签: spring transactions ehcache


    【解决方案1】:

    是的,有办法实现你的目标。

    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;
              }
      
          }
      
      }
      

    源码和测试用例可以在here找到

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

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

    1. 在事务提交之前,被逐出的密钥在事务内部仍可访问
    2. putIfAbsent 操作未延期

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

    【讨论】:

      【解决方案2】:

      你不想要本地事务,你想要 Ehcache 支持的 XA 事务。

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

      【讨论】:

        猜你喜欢
        • 2017-03-04
        • 2011-03-22
        • 1970-01-01
        • 1970-01-01
        • 2014-06-04
        • 1970-01-01
        • 1970-01-01
        • 2023-03-20
        • 2016-08-24
        相关资源
        最近更新 更多