Search code examples
javaspring-bootmoduleliquibase

Springboot external library Liquibase migration


I have an external SpringBoot commons library (built as a jar) and a main project that includes it.

The external library requires a set of default database tables. For this, I have set up Liquibase. The main project also makes us of Liquibase. Both projects have different changelog files. Suppose one is named library.xml and the other main.xml. All database and Liquibase properties are configured in the main project (as well as spring.liquibase.change-log=main.xml).

The library has a single property: spring.liquibase.change-log=library.xml.

How can I tell Liquibase "Run both library.xml and main.xml"? I suppose there is something I need to configure in the library project, since the main application should use it as is (simply by importing and using it).


Solution

  • To combine/structure our (liquibase) change logs, we can:

    1. Use () include "attribute" (et al.). (I'd still prefer this!)
    2. (With ) Define multiple @Bean SpringLiquibases:

    Spring-Boot (2.7.5) solution:

    (main project) application.properties:

    spring.liquibase.change-log=classpath:/main.xml
    # many more with same prefix ...
    lib.liquibase.change-log=classpath:/library.xml
    # according for lib.liquibase.* ...
    

    Main/Config class:

    @SpringBootApplication(/* scanBasePackages = "com.example.lib" */)
    // We need/want these:
    @EnableConfigurationProperties(LiquibaseProperties.class)
    public class Demo {
    
      @Bean // "main" liquibase: 
      // when order matters: @DependsOn("liquibaseLib") 
      @ConfigurationProperties("spring.liquibase") // !
      public SpringLiquibase liquibase( 
          ObjectProvider<DataSource> dataSource,
          @LiquibaseDataSource ObjectProvider<DataSource> liquibaseDataSource,
          LiquibaseProperties properties) {
        return liqui(dataSource, liquibaseDataSource, properties);
      }
    
      @Bean // lib liquibase:
      @ConfigurationProperties("lib.liquibase")
      public SpringLiquibase liquibaseLib(
          ObjectProvider<DataSource> dataSource,
          /* alternatively: @MyVeryCustomDataSource ... */
          @LiquibaseDataSource ObjectProvider<DataSource> liquibaseDataSource,
          LiquibaseProperties properties) {
        return liqui(dataSource, liquibaseDataSource, properties);
      }
    
      // pstvm...
    
      private SpringLiquibase liqui(ObjectProvider<DataSource> dataSource,
          ObjectProvider<DataSource> liquibaseDataSource, LiquibaseProperties properties) {
        // trick: Don't use LiquibaseConfiguration as `@Configuration/Bean`
        // , it is not "repeatable" (in regards of liquibase execution;(
        // , but rather as a "utility object"/factory:
        LiquibaseConfiguration helpr = new LiquibaseConfiguration(properties);
        return helpr.liquibase(dataSource, liquibaseDataSource);
      }
    }
    

    Header (package+imports) of Demo.java:

    package com.example.app;import javax.sql.DataSource; import liquibase.integration.spring.SpringLiquibase; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration.LiquibaseConfiguration; import org.springframework.boot.autoconfigure.liquibase.LiquibaseDataSource; import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.DependsOn;

    See Also.


    Simple Solution

    ...as shown by 2:

    import liquibase.integration.spring.SpringLiquibase;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.DependsOn;
    import javax.sql.DataSource;
    
    @Configuration // scanned/imported/used by "main" !
    public class MultipleLiquiaseConfiguration {
    
        @Bean
        public SpringLiquibase liquibaseLib(DataSource dataSource) { // !
            SpringLiquibase liquibase = new SpringLiquibase();
            liquibase.setDataSource(dataSource);
            liquibase.setChangeLog("classpath:library.xml"); // !
            return liquibase;
        }
    
        @Bean
        // when order matters: @DependsOn("liquibaseLib")
        public SpringLiquibase liquibaseMain(DataSource dataSource) { // !
            SpringLiquibase liquibase = new SpringLiquibase();
            liquibase.setDataSource(dataSource);
            liquibase.setChangeLog("classpath:main.xml"); // !
            return liquibase;
        }
    }
    

    Regarding:

    The library has a single property...

    I hope it is a test property;)!