我已经挣扎了几天才能让这个工作,但似乎我无法找到解决方案。这就是为什么我想在这里问一下。
简短版
我有一个多租户实现,可以使用Spring启动,Spring Data JPA和Hibernate。这就像一个魅力。但是现在我想实现一个功能,我在单个事务中切换数据库(数据源)。例如,我在服务类中使用类似的代码
@Autowired
private CustomRepository customRepository;
@Autorwired
private CustomTenantIdentifierResolver customResolver;
@Transactional
public Custom getCustom(String name) {
// Set the datasource to "one";
this.customResolver.setIdentifier("one");
Custom result = this.customRepository.findOneByName(name);
//If the result is null, switch datasource to default and try again
this.customResolver.setIdentifier("default");
result = this.customRepository.findOneByName(name);
return result;
}
问题是,我的数据源没有切换。它对第二个请求使用相同的源。我想我在这里做了一些非常错误的事情。
在单个交易期间切换数据源的正确方法是什么?
编辑(07-06-2016)
由于我注意到为单个事务切换数据源不起作用,我将添加后续内容。
是否可以针对单个用户请求在两个事务之间切换数据源?如果是这样,那么这样做的正确方法是什么?
长版
在继续之前,我想提一下我的多租户实施基于tutorial provided on this blog。
现在,我的目标是在动态的(由自定义标识符选择)无法找到结果时使用默认数据源作为后备。所有这些都需要在单个用户请求中完成。在解决方案中使用单个或多个事务注释方法并没有什么区别。
直到现在我尝试了几件事,其中一件如上所述,另一件包括使用多个交易管理器。该实现使用配置文件来创建两个事务管理器bean,每个bean都是不同的数据源。
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
@Autowired
private EntityManagerFactory entityManagerFactory;
@Autowired
private DataSourceProvider dataSourceProvider;
@Bean(name = "defaultTransactionManager")
public PlatformTransactionManager defaultTransactionManager() {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setEntityManagerFactory(entityManagerFactory);
jpaTransactionManager.setDataSource(dataSourceProvider.getDefaultDataSource());
jpaTransactionManager.afterPropertiesSet();
return jpaTransactionManager;
}
@Bean(name = "dynamicTransactionManager")
public PlatformTransactionManager dynamicTransactionManager() {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setEntityManagerFactory(entityManagerFactory);
jpaTransactionManager.afterPropertiesSet();
return jpaTransactionManager;
}
}
接下来,我将服务方法拆分为两个独立的方法,并添加@Transactional
注释,包括正确的bean名称
@Transactional("dynamicTransactionManager")
public Custom getDynamicCustom(String name) {
...stuff...
}
@Transactional("defaultTransactionManager")
public Custom getDefaultCustom(String name) {
...stuff...
}
但它没有任何区别,第一个数据源仍然用于第二个方法调用(应该使用默认的事务管理器)。
我希望有人可以帮我找到解决方案。
提前谢谢。
答案 0 :(得分:1)
Spring提供了DataSource的变体,称为 AbstractRoutingDatasource 。它可以用来代替标准的DataSource实现,并启用一种机制来确定在运行时为每个操作使用哪个具体的DataSource。您需要做的就是扩展它并提供一个抽象的determineCurrentLookupKey方法的实现。
请记住,只要 TransactionsManager 请求连接,就会调用 determineCurrentLookupKey 方法。因此,如果要切换DataSource,只需打开新事务。
你可以在这里找到例子 http://fedulov.website/2015/10/14/dynamic-datasource-routing-with-spring/
答案 1 :(得分:0)
您无法将交易转移到其他数据源。虽然存在分布式(或XA)事务的概念,但它由单独的事务(在单独的数据源中)组成,这些事务被视为它们是单个(分布式)事务的一部分。
答案 2 :(得分:0)
我不知道是否可能,但我认为你应该尽量避免在交易过程中切换来源,原因如下:
如果在第二个请求期间发生错误,您将要回滚整个事务,这意味着切换回旧源。为了能够做到这一点,您需要保持与旧资源的开放连接:当交易完成后,您需要确认该旧交易来源的交易。
我建议重新考虑一下,如果你真的想要这个,除非有可能。