如何在 Spring 中为多个数据源设置 liquibase?

How to set up liquibase in Spring for multiple data sources?

我需要为Spring中的两个datasources设置liquibase,目前好像只能设置一个liquibase,你可以选择对于哪个数据源。

只有 2 个数据源和 2 个 bean

<bean id="liquibase1" class="liquibase.integration.spring.SpringLiquibase">
      <property name="dataSource" ref="dataSource1" />
      <property name="changeLog" value="classpath:db1-changelog.xml" />
 </bean>
 <bean id="liquibase2" class="liquibase.integration.spring.SpringLiquibase">
      <property name="dataSource" ref="dataSource2" />
      <property name="changeLog" value="classpath:db2-changelog.xml" />
 </bean>

如果您使用 spring 启动,这里是可以帮助您的设置:

配置class:

@Configuration
public class DatasourceConfig {

    @Primary
    @Bean
    @ConfigurationProperties(prefix = "datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "datasource.primary.liquibase")
    public LiquibaseProperties primaryLiquibaseProperties() {
        return new LiquibaseProperties();
    }

    @Bean
    public SpringLiquibase primaryLiquibase() {
        return springLiquibase(primaryDataSource(), primaryLiquibaseProperties());
    }

    @Bean
    @ConfigurationProperties(prefix = "datasource.secondary")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "datasource.secondary.liquibase")
    public LiquibaseProperties secondaryLiquibaseProperties() {
        return new LiquibaseProperties();
    }

    @Bean
    public SpringLiquibase secondaryLiquibase() {
        return springLiquibase(secondaryDataSource(), secondaryLiquibaseProperties());
    }

    private static SpringLiquibase springLiquibase(DataSource dataSource, LiquibaseProperties properties) {
        SpringLiquibase liquibase = new SpringLiquibase();
        liquibase.setDataSource(dataSource);
        liquibase.setChangeLog(properties.getChangeLog());
        liquibase.setContexts(properties.getContexts());
        liquibase.setDefaultSchema(properties.getDefaultSchema());
        liquibase.setDropFirst(properties.isDropFirst());
        liquibase.setShouldRun(properties.isEnabled());
        liquibase.setLabels(properties.getLabels());
        liquibase.setChangeLogParameters(properties.getParameters());
        liquibase.setRollbackFile(properties.getRollbackFile());
        return liquibase;
    }


...

}

properties.yml

datasource:
  primary:
    url: jdbc:mysql://localhost/primary
    username: username
    password: password
    liquibase:
      change-log: classpath:/db/changelog/db.primary.changelog-master.xml
  secondary:
    url: jdbc:mysql://localhost/secondary
    username: username
    password: password
    liquibase:
      change-log: classpath:/db/changelog/db.secondary.changelog-master.xml

您还可以 运行 多个 liquibase 实例(即不仅限于主要和次要实例)。

例如您的配置 java 可以有:

@Bean
@ConfigurationProperties(prefix = "liquibase1")
...
@Bean
@ConfigurationProperties(prefix = "liquibase2")
...
@Bean
@ConfigurationProperties(prefix = "liquibase3")

你的 application.property 可以有:

liquibase1.default-schema=schemaA
...
liquibase2.default-schema=schemaB
...
liquibase3.default-schema=schemaC
...

而且(令人兴奋的是)这些 springLiquibase 实例可以使用相同的数据源,或不同的数据源...随您喜欢。

运行顺序?我没有找到任何官方文档,根据我在调试中的观察,所有liquibase迁移运行都是按照你在application.properties中写的顺序进行的。那些想要 运行 在一个数据源中迁移,然后转到另一个数据源,然后返回到该数据源和 运行 其他东西的人,您可能想尝试这种多 liquibase 实例方法。

我做了一个项目,我可以用你的特定变更集创建多个数据源,所以如果你需要添加另一个数据源,它会改变你的 application.yml,不再需要更改代码。

配置class

@Configuration
@ConditionalOnProperty(prefix = "spring.liquibase", name = "enabled", matchIfMissing = true)
@EnableConfigurationProperties(LiquibaseProperties.class)
@AllArgsConstructor
public class LiquibaseConfiguration {

    private LiquibaseProperties properties;
    private DataSourceProperties dataSourceProperties;

    @Bean
    @DependsOn("tenantRoutingDataSource")
    public MultiTenantDataSourceSpringLiquibase liquibaseMultiTenancy(Map<Object, Object> dataSources,
                                                                      @Qualifier("taskExecutor") TaskExecutor taskExecutor) {
        // to run changeSets of the liquibase asynchronous
        MultiTenantDataSourceSpringLiquibase liquibase = new MultiTenantDataSourceSpringLiquibase(taskExecutor);
        dataSources.forEach((tenant, dataSource) -> liquibase.addDataSource((String) tenant, (DataSource) dataSource));
        dataSourceProperties.getDataSources().forEach(dbProperty -> {
            if (dbProperty.getLiquibase() != null) {
                liquibase.addLiquibaseProperties(dbProperty.getTenantId(), dbProperty.getLiquibase());
            }
        });

        liquibase.setContexts(properties.getContexts());
        liquibase.setChangeLog(properties.getChangeLog());
        liquibase.setDefaultSchema(properties.getDefaultSchema());
        liquibase.setDropFirst(properties.isDropFirst());
        liquibase.setShouldRun(properties.isEnabled());
        return liquibase;
    }

}

application.yml

spring:
  dataSources:
    - tenantId: db1
      url: jdbc:postgresql://localhost:5432/db1
      username: postgres
      password: 123456
      driver-class-name: org.postgresql.Driver
      liquibase:
        enabled: true
        default-schema: public
        change-log: classpath:db/master/changelog/db.changelog-master.yaml
    - tenantId: db2
      url: jdbc:postgresql://localhost:5432/db2
      username: postgres
      password: 123456
      driver-class-name: org.postgresql.Driver
    - tenantId: db3
      url: jdbc:postgresql://localhost:5432/db3
      username: postgres
      password: 123456
      driver-class-name: org.postgresql.Driver

Link 的存储库:https://github.com/dijalmasilva/spring-boot-multitenancy-datasource-liquibase

我需要支持动态数量的数据源,而不是固定数量的数据源。我发现您可以通过创建这样的服务为多个数据源使用相同的 SpringLiquibase bean:

@Service
@DependsOn("liquibase")
public class LiquibaseService {

    @Autowired
    @Qualifier("liquibase")
    private SpringLiquibase liquibase;

    @PostConstruct
    public void initialize() {

        /* Obtain datasources from wherever. I obtain them from a master DB. It's up to you. */
        List<DataSource> dataSources = obtainDataSources();

        for (DataSource dataSource : dataSources) {
            try {
                liquibase.setDataSource(dataSource);
                liquibase.setChangeLog("classpath:liquibase/emp.changelog.xml");
                liquibase.setShouldRun(true);

                // This runs Liquibase
                liquibase.afterPropertiesSet();

            } catch (LiquibaseException ex) {
                throw new RuntimeException(ex);
            }
        }
    }

}

为此,您应该在某处声明一个 SpringLiquibase bean。在这个例子中,我在我的一个配置文件中得到了这个:

@Bean
public SpringLiquibase liquibase(LiquibaseProperties properties) {

    SpringLiquibase liquibase = new SpringLiquibase();
    liquibase.setDataSource(systemDataSource);
    liquibase.setChangeLog("classpath:liquibase/sis.changelog.xml");
    liquibase.setContexts(properties.getContexts());
    liquibase.setDefaultSchema(properties.getDefaultSchema());
    liquibase.setDropFirst(properties.isDropFirst());


    liquibase.setLabels(properties.getLabels());
    liquibase.setChangeLogParameters(properties.getParameters());
    liquibase.setRollbackFile(properties.getRollbackFile());

    // This is because we are running the process manually. Don't let SpringLiquibase do it.
    liquibase.setShouldRun(false);

    return liquibase;
}

以上内容很大程度上取决于您的数据源配置要求。您可能还需要将它放在主应用程序 class 上,这样 Spring-Liquibase 自动配置就不会启动:

@SpringBootApplication(exclude = {
    LiquibaseAutoConfiguration.class
})
public class Application {

    // Stuff...

}