我正在尝试设置事务性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一起使用?
答案 0 :(得分:3)
是的,有办法实现目标。
因为您有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());
}
您需要指定默认情况下应使用的事务管理器:
@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
来装饰缓存以推迟操作直到事务结束。但它有以下缺点:
putIfAbsent
操作未被推迟这对我来说是一个问题,所以我以不同的方式实现了这个功能。检查'me.qnox.springframework.cache.tx.TxAwareCacheManagerProxy',上面描述的问题解决了,在同一个存储库中
答案 1 :(得分:0)
您不希望本地交易,您想要Ehcache支持的XA交易。
查看Ehcache 2.10.x或Ehcache 2.8.x的文档。