我正在尝试使用 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 一起工作?
最佳答案
是的,有一种方法可以实现您的目标。
因为您有 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()); }
您需要指定默认情况下应使用哪个事务管理器:
@Configuration public class Configuration implements TransactionManagementConfigurer { ... @Override public PlatformTransactionManager annotationDrivenTransactionManager() { return transactionManager(); } ... }
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
它装饰缓存以推迟操作直到事务结束。但它有以下缺点:
- 在事务提交之前,被逐出的 key 在事务内仍可访问
-
putIfAbsent
操作不推迟
这对我来说是一个问题,所以我以不同的方式实现了这个功能。检查'me.qnox.springframework.cache.tx.TxAwareCacheManagerProxy',上述问题已解决,在同一个存储库中
关于spring - Ehcache本地事务与Spring @Transactional,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31944204/