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

问题描述 投票:0回答:4

我已经努力了几天才能使其正常工作,但似乎找不到解决方案。所以我想在这里问一下。

精简版

我有一个多租户实现,可与 Spring boot、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)
由于我注意到切换单个事务的数据源是行不通的,所以我将添加后续操作。

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

长版

在继续之前,我想提一下,我的多租户实现是基于此博客上提供的教程

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

到目前为止,我尝试了几件事,其中之一如上所述,另一个包括使用多个事务管理器。该实现使用配置文件来创建两个事务管理器 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...
}

但是没有任何区别,第一个数据源仍然用于第二个方法调用(应该使用默认的事务管理器)。

我希望有人能帮我找到解决方案。
预先感谢。

java spring-data spring-data-jpa multi-tenant
4个回答
2
投票

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

请记住,每当 TransactionsManager 请求连接时,都会调用 defineCurrentLookupKey 方法。所以,如果你想切换DataSource,你只需要打开新的事务即可。

您可以在这里找到示例 http://fedulov.website/2015/10/14/dynamic-datasource-routing-with-spring/


0
投票

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


0
投票

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

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

如果你真的想要这个,我建议你重新考虑一下,如果有可能的话。


0
投票

这里同样的问题 - MultitenantDataSource 的工作方式就像一个魅力,但如果您想在同一请求中切换租户,您将继续获得第一次解析数据源时的结果。

子类 AbstractRoutingDataSource 中的 certainCurrentLookupKey() 方法似乎每个会话/请求仅调用一次。

我尝试通过ApplicationContext多次获取Repository Bean(在更改LookupKey之后),但没有帮助。

我在 REST 应用程序中的解决方法: 我使用 RestTemplate 来调用我自己的服务 - 然后每次都会正确解析数据源。

© www.soinside.com 2019 - 2024. All rights reserved.