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

前端 未结 5 1750
醉话见心
醉话见心 2020-12-25 07:59

I need to set up liquibase for two datasources in Spring, at the moment it seems that only one liquibase set up is possib

相关标签:
5条回答
  • 2020-12-25 08:37

    If you are using spring boot, here is the setup which can help you:

    Configuration 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
    
    0 讨论(0)
  • 2020-12-25 08:37

    I've done a project that I can create multiple dataSources with your specific changeSets, so if you need to add another dataSource, it would just change your application.yml, no longer needing to change the code.

    Configuration 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 of repository: https://github.com/dijalmasilva/spring-boot-multitenancy-datasource-liquibase

    0 讨论(0)
  • 2020-12-25 08:46

    I was in the need of supporting a dynamic amount of DataSources, not a fixed number of them. I found that you can use the same SpringLiquibase bean for multiple DataSources by making a service like this:

    @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);
                }
            }
        }
    
    }
    

    For this to work, you should have a SpringLiquibase bean declared somewhere. In this example, I got this in one of my configuration files:

    @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;
    }
    

    The above highly depends on your DataSource configuration requirements. You could also need to put this on your main Application class so the Spring-Liquibase auto-configuration doesn't kick in:

    @SpringBootApplication(exclude = {
        LiquibaseAutoConfiguration.class
    })
    public class Application {
    
        // Stuff...
    
    }
    
    0 讨论(0)
  • 2020-12-25 08:50

    You can also run multiple liquibase instance (i.e. not only limit to a primary and a secondary).

    e.g. Your configuration java can have:

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

    Your application.property can have:

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

    And (excitingly), these springLiquibase instances can use the same Datasource, or different DataSource... however you like it.

    Running order? I haven't found any official document, from my observation in debug, all liquibase migration runs according to the order you write in application.properties. Those who wants to run migration in one datasource, then go to another datasource, then come back to this datasource and run something else,,, you may want to try this multiple liquibase instance approach.

    0 讨论(0)
  • 2020-12-25 08:59

    Just have 2 datasources and 2 beans

    <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>
    
    0 讨论(0)
提交回复
热议问题