spring - Ehcache本地事务与Spring @Transactional

标签 spring transactions ehcache

我正在尝试使用 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表示本地交易受到明确控制:

Local transactions are not controlled by a Transaction Manager. Instead there is an explicit API where a reference is obtained to a TransactionController for the CacheManager using cacheManager.getTransactionController() and the steps in the transaction are called explicitly

但这会很困难,因为我想将我的ehcache事务与数据库事务同步,而数据库事务是由@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;
            }
    
        }
    
    }
    

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

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

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

  1. 在事务提交之前,被逐出的 key 在事务内仍可访问
  2. putIfAbsent操作不推迟

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

关于spring - Ehcache本地事务与Spring @Transactional,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31944204/

相关文章:

java - Spring Websocket + SockJS 中 SimpleMessageBroker 的/topic 和/queue 之间的区别

mysql - 根据主表主键值向子表插入数据

java - Hibernate 配置 - NoCacheRegionFactoryAvailableException

Spring-Boot - 激活休眠二级缓存

java - @Value 注解不从属性文件中注入(inject)值

spring - Spring Cloud 配置服务器中是否可以有多个 GIt 主机?

java - Spring在从文件系统加载配置文件时无法解析占位符

asp.net - "Communication with the underlying transaction manager has failed"错误消息

sql - 在 MS SQL Server 中,有没有办法增加 "atomically"用作计数器的列?

java - 使用 ehcache 的 Hibernate 二级缓存(使用 RMI 进行复制)