Java 使用多租户实现在单个事务期间切换数据源

Java 使用多租户实现在单个事务期间切换数据源,java,spring-data,spring-data-jpa,multi-tenant,Java,Spring Data,Spring Data Jpa,Multi Tenant,我已经努力工作了几天,但似乎我找不到解决办法。这就是为什么我想在这里问这个问题 短版 我有一个多租户实现,它与SpringBoot、SpringDataJPA和Hibernate一起工作。这很有魅力。但是现在我想实现一个功能,在单个事务中切换数据库(数据源)。例如,我在服务类中使用类似的代码 @Autowired private CustomRepository customRepository; @Autorwired private CustomTenantIdentifierResolv

我已经努力工作了几天,但似乎我找不到解决办法。这就是为什么我想在这里问这个问题

短版

我有一个多租户实现,它与SpringBoot、SpringDataJPA和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)
因为我注意到为单个事务切换数据源是行不通的,所以我将添加一个后续内容

对于单个用户请求,是否可以在两个事务之间切换数据源?如果是,正确的方法是什么

长版本

在继续之前,我想提到我的多租户实现是基于

现在,我的目标是在动态数据源(由自定义标识符选择)找不到结果时,使用默认数据源作为备用数据源。所有这些都需要在单个用户请求中完成。使用单个或多个事务性注释方法的解决方案没有什么区别

到目前为止,我尝试了几种方法,其中一种如上所述,另一种包括使用多个事务管理器。该实现使用一个配置文件来创建两个事务管理器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...
}
但是这没有任何区别,第一个数据源仍然用于第二个方法调用(应该使用默认的事务管理器)

我希望有人能帮我找到解决办法。

提前感谢。

您不能将事务转移到另一个数据源。虽然有分布式(或XA)事务的概念,但它由单独的事务(在单独的数据源中)组成,这些事务被视为单个(分布式)事务的一部分。

我不知道这是否可行,但我认为您应该尽量避免在事务期间切换源,原因如下:

如果在第二个请求期间发生错误,您将希望回滚整个事务,这意味着切换回旧的源。为了能够做到这一点,您需要保持与该旧源的开放连接:当事务完成时,您需要确认与该旧源的事务


如果您真的想这样做,我建议您重新考虑一下,如果可能的话,除了这一点之外。

Spring提供了一种数据源变体,称为AbstractRoutingDatasource。它可以用来代替标准的数据源实现,并支持一种机制来确定在运行时为每个操作使用哪个具体的数据源。您所需要做的就是扩展它并提供一个抽象determineCurrentLookupKey方法的实现

请记住,只要TransactionManager请求连接,就会调用determineCurrentLookupKey方法。所以,如果您想切换数据源,只需要打开新的事务

你可以在这里找到例子

有道理。但是,在一个用户请求中使用两个单独的事务切换数据源是否可能?理论上是的,我对spring框架的了解是不存在的。可能是(部分)每个请求仅限于一个事务。这是有道理的,如果请求在第二个事务中失败,您将如何通知客户第一个部分成功,而第二个部分没有。对于客户来说,执行两个单独的请求不是更好吗?每个请求都有自己的责任。我的意思是:后退一步,看看功能,然后想知道你是如何进入这种情况的。我有点期待在事务期间尝试切换数据源会破坏一切。但是,您知道当我在一个用户请求中使用两个单独的事务时,是否可以切换数据源吗?这听起来并非完全不可行,但我对实现不太熟悉,无法提供帮助。您好,仅链接的答案不适合StackOverflow。请在您的答案中添加更多详细信息。您曾经找到过解决此问题的方法吗?不,抱歉。一旦Spring请求来自数据源的连接,它就会在整个事务/会话中使用相同的连接。然而,有一种可能的解决方案涉及到使用多个EntityManager,尽管我还没有对其进行测试,我不确定这是否允许您动态切换EntityManager。一个快速的谷歌让我想到这一点:在一个新的线程中做第二个事务怎么样?因为事务与线程关联