Saltar al contenido

¿Cómo configurar Liquibase en Spring para múltiples fuentes de datos?

Posterior a de una extensa búsqueda de datos dimos con la solución esta escollo que tienen ciertos de nuestros lectores. Te dejamos la solución y nuestro deseo es que te resulte de mucha ayuda.

Solución:

Si está usando Spring Boot, aquí está la configuración que puede ayudarlo:

Clase de configuración:

@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

Hice un proyecto en el que puedo crear múltiples fuentes de datos con sus conjuntos de cambios específicos, por lo que si necesita agregar otra fuente de datos, simplemente cambiaría su application.yml, sin necesidad de cambiar el código.

Clase de configuración

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

Enlace del repositorio: https://github.com/dijalmasilva/spring-boot-multitenancy-datasource-liquibase

Necesitaba admitir una cantidad dinámica de DataSources, no un número fijo de ellos. Descubrí que puedes usar lo mismo SpringLiquibase bean para múltiples fuentes de datos haciendo un servicio como este:

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


Para que esto funcione, debe tener un SpringLiquibase bean declarado en alguna parte. En este ejemplo, obtuve esto en uno de mis archivos de configuración:

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

Lo anterior depende en gran medida de los requisitos de configuración de su fuente de datos. También podría necesitar poner esto en su clase de aplicación principal para que la configuración automática de Spring-Liquibase no se active:

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

    // Stuff...


Comentarios y valoraciones

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *