我正在尝试设置事务性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一起工作?
是的,有一种方法可以实现你的目标。
-
因为你有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; } } }
源代码和测试用例可以在这里找到
这个解决方案有一个明显的缺点ehcache的事务协调器不支持挂起/恢复操作,所以内部事务(PROPAGATION_REQUIRES_NEW)是不可能的。这就是为什么我必须另找一个。
另一种选择是根本不使用本地ehcache事务,而是使用org.springframework.cache.transaction.AbstractTransactionSupportingCacheManager#setTransactionAware
来装饰缓存以延迟操作,直到事务结束。但它有以下缺点:
- 被驱逐的键在事务内保持可访问,直到事务提交
-
putIfAbsent
操作未延迟
这对我来说是个问题,所以我用不同的方式实现了这个功能。检查"me.qnox.springframework.cache.tx。TxAwareCacheManagerProxy',上面描述的问题已经解决,在同一个存储库
您不需要本地事务,您需要XA事务,这是Ehcache支持的。
查看Ehcache 2.10的文档。