在Spring启动中以非常干净的方式配置两个数据源

时间:2017-04-13 20:29:21

标签: spring spring-boot spring-data spring-data-jpa hikaricp

好的,我已经阅读了相当多的帖子,并阅读了关于此的春季启动文档,但从未真正得到一个更清洁的方法的答案。

这里是我的用例:我们有100-200个oracle实体,我们正在使用JPARepository接口来查询它们。现在我们需要确保读取db用于读取调用,写入db应该用于任何写入。

我们有一个Spring启动应用程序,使用HikariCP数据源并使用@EnableTransactionManagement,@ EnableJpaRepositories通过实体管理器,事务管理器和基础包的引用进行配置来进行扫描。

我创建了两个配置文件,一个配置了ReadConfiguration,另一个配置了WriteConfiguration。现在的问题是,我们有一个标准OO方式的代码,我们有服务和存储库层。不同的服务正在注入各种存储库。每个存储库接口都在扩展JpaRepository,并且该接口在许多服务类中自动装配。

我想要实现的是使用相同的存储库层,但不知何故,存储库层应该自动知道如果它是一个读取调用,则使用读取数据源,如果它是一个写入调用,则使用写入数据源。

其中一个解决方案是使用某种方式来破解存储库的代理实现,以便有一些逻辑来查看它是否已经读取调用然后使用此读取数据源,如果它是一个写入调用然后使用写数据源。有没有人以这种方式解决这个问题?基本上我需要的是根据方法调用将调用路由到数据源。如果方法正在进行一些读操作,那么我需要确保它使用read datasource或者使用write datasource。

任何人都可以指出如何在不编写新的存储库层进行读写的情况下实现这种架构。

感谢。

3 个答案:

答案 0 :(得分:2)

我想你可以使用这个repo中提到的AbstractRoutingDataSource。 https://github.com/kwon37xi/replication-datasource

使用这种方法,您可以添加带有额外属性的@Transactional方法来指示其读取还是写入。 @Transactional(readOnly = true | false)

答案 1 :(得分:1)

我不认为这实际上是可能的(见下文),但这种方法至少可以接近。

我从LazyConnectionDataSource的代码开始,因为根据您的要求,在您真正知道要对它做什么之前,您不能获得连接。

为它提供对会话的引用,因此当它实际需要Connection时,它会检查会话,查看它是否是脏的,并根据实际使用的DataSource来确定。

现在我不认为这实际上会起作用。在正常用例中,事务包括:

  1. 阅读一些数据

  2. 根据它进行一些更改

  3. 坚持这些变化。

  4. 这些步骤严格按顺序进行,虽然我们在Java中有类似Future的类型但我们实际上无法展望未来,因此无法在第一步决定是否会有第三步。

答案 2 :(得分:0)

我的猜测是,这可以被视为一种多租户方式,一个租户将只读,另一个租户将

为多租户支持配置持久层的步骤包括:

  • Hibernate,JPA和数据源属性。类似的东西:

<强> application.yml

...
multitenancy:
  dvdrental:
    dataSources:
      -
        tenantId: readonly
        url: jdbc:postgresql://172.16.69.133:5432/db_dvdrental
        username: user_dvdrental
        password: changeit
        driverClassName: org.postgresql.Driver
      -
        tenantId: write
        url: jdbc:postgresql://172.16.69.133:5532/db_dvdrental
        username: user_dvdrental
        password: changeit
        driverClassName: org.postgresql.Driver
...

<强> MultiTenantJpaConfiguration.java

 ...
 @Configuration
 @EnableConfigurationProperties({ MultiTenantDvdRentalProperties.class, JpaProperties.class })
 @ImportResource(locations = { "classpath:applicationContent.xml" })
 @EnableTransactionManagement
 public class MultiTenantJpaConfiguration {

   @Autowired
   private JpaProperties jpaProperties;

   @Autowired
   private MultiTenantDvdRentalProperties multiTenantDvdRentalProperties;
 ...
 }

<强> MultiTenantDvdRentalProperties.java

...
@Configuration
@ConfigurationProperties(prefix = "multitenancy.dvdrental")
public class MultiTenantDvdRentalProperties {

  private List<DataSourceProperties> dataSourcesProps;
  // Getters and Setters

  public static class DataSourceProperties extends org.springframework.boot.autoconfigure.jdbc.DataSourceProperties {

    private String tenantId;
    // Getters and Setters
  }
}
  • 数据源bean

<强> MultiTenantJpaConfiguration.java

 ...
 public class MultiTenantJpaConfiguration {
 ...
   @Bean(name = "dataSourcesDvdRental" )
   public Map<String, DataSource> dataSourcesDvdRental() {
       ...
   }
 ...
 }
  • 实体经理工厂bean

<强> MultiTenantJpaConfiguration.java

 ...
 public class MultiTenantJpaConfiguration {
 ...
   @Bean
   public MultiTenantConnectionProvider multiTenantConnectionProvider() {
       ...
   }

   @Bean
   public CurrentTenantIdentifierResolver currentTenantIdentifierResolver() {
       ...
   }

   @Bean
   public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(MultiTenantConnectionProvider multiTenantConnectionProvider,
     CurrentTenantIdentifierResolver currentTenantIdentifierResolver) {
       ...  
   }
 ...
 }
  • 事务管理器bean

<强> MultiTenantJpaConfiguration.java

 ...
 public class MultiTenantJpaConfiguration {
 ...
   @Bean
   public EntityManagerFactory entityManagerFactory(LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
       ...
   }

   @Bean
   public PlatformTransactionManager txManager(EntityManagerFactory entityManagerFactory) {
       ...
   }
 ...
 }
  • Spring Data JPA和事务支持配置

<强> applicationContent.xml

...
<jpa:repositories base-package="com.asimio.dvdrental.dao" transaction-manager-ref="txManager" />
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true" />
...

<强> ActorDao.java

public interface ActorDao extends JpaRepository<Actor, Integer> {
}

根据您的需要,可以这样做:

...
@Autowired
private ActorDao actorDao;
...

// Read feature
...
DvdRentalTenantContext.setTenantId("readonly");
this.actorDao.findOne(...);
...

// Or write
DvdRentalTenantContext.setTenantId("write");
this.actorDao.save(...);
...

设置tenantId可以在servlet过滤器/ Spring MVC拦截器/线程中完成,它将执行JPA操作等。

有关多租户方法的更多详细信息,请访问我的博客http://tech.asimio.net/2017/01/17/Multitenant-applications-using-Spring-Boot-JPA-Hibernate-and-Postgres.html