'Spring Boot. Running liquibase changelog after jpa auto-dll tables generation on hsqldb
Case is like this. I have liquibase changelog contaning only inserts. I am trying to force Spring Boot to initialize database (hsqldb) schema using JPA based on @Entities and later execute liquibase changelog. Unfortunatelly Spring Boot is doing it in oposite order.
I checked LiquibaseAutoConfiguration and it has:
@AutoConfigureAfter({ DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class })
so it is executed after HibernateJpaAutoConfiguration however Spring Boot still do it not the way I wish ;).
Spring Boot version: 1.3.0.RELEASE Liquibase-core version: 3.5.1
Thank you in advance for any naswer
Solution 1:[1]
Possible solution is to disable automatic boot liquibase run via application.properties:
spring.jpa.hibernate.ddl-auto=create
liquibase.enabled=false
and then manually configure SpringLiquibase bean to depends on entityManagerFactory:
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.DependsOn;
import liquibase.integration.spring.SpringLiquibase;
@SpringBootApplication
public class DemoApplication {
@Autowired
private DataSource dataSource;
@Bean
public LiquibaseProperties liquibaseProperties() {
return new LiquibaseProperties();
}
@Bean
@DependsOn(value = "entityManagerFactory")
public SpringLiquibase liquibase() {
LiquibaseProperties liquibaseProperties = liquibaseProperties();
SpringLiquibase liquibase = new SpringLiquibase();
liquibase.setChangeLog(liquibaseProperties.getChangeLog());
liquibase.setContexts(liquibaseProperties.getContexts());
liquibase.setDataSource(getDataSource(liquibaseProperties));
liquibase.setDefaultSchema(liquibaseProperties.getDefaultSchema());
liquibase.setDropFirst(liquibaseProperties.isDropFirst());
liquibase.setShouldRun(true);
liquibase.setLabels(liquibaseProperties.getLabels());
liquibase.setChangeLogParameters(liquibaseProperties.getParameters());
return liquibase;
}
private DataSource getDataSource(LiquibaseProperties liquibaseProperties) {
if (liquibaseProperties.getUrl() == null) {
return this.dataSource;
}
return DataSourceBuilder.create().url(liquibaseProperties.getUrl())
.username(liquibaseProperties.getUser())
.password(liquibaseProperties.getPassword()).build();
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
However I'd strongly encourage to use liquibase to build schema as well. I believe it was designed (see org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration.LiquibaseJpaDependencyConfiguration) to run before hibernate's ddl-auto so that it's possible to set ddl-auto=validate and have liquibase schema validated by hibernate.
Solution 2:[2]
The solution provided by Radek Posto?owicz served me quite some time but didn't work anymore after updating to spring-boot 2.5.0. I think it can be fully replaced by adding the following property to application.properties (or yml):
spring.jpa.defer-datasource-initialization=true
This is also mentioned in the release notes.
Solution 3:[3]
I just updated Spring Boot to 2.5.3 and have the same problem.
I solved the issue by using a class CustomSpringLiquibase (Kotlin version) :
class CustomSpringLiquibase(
private var springLiquibase: SpringLiquibase
) : InitializingBean, BeanNameAware, ResourceLoaderAware {
companion object {
private val LOGGER = LoggerFactory.getLogger(CustomSpringLiquibase::class.java)
}
@Throws(LiquibaseException::class)
override fun afterPropertiesSet() {
LOGGER.info("Init Liquibase")
springLiquibase.afterPropertiesSet()
}
override fun setBeanName(name: String) {
springLiquibase.beanName = name
}
override fun setResourceLoader(resourceLoader: ResourceLoader) {
springLiquibase.resourceLoader = resourceLoader
}
}
And in my SpringBootApplication class I added the following (Java Version):
@Bean
@DependsOn(value = "entityManagerFactory")
public CustomSpringLiquibase liquibase() {
LiquibaseProperties liquibaseProperties = liquibaseProperties();
SpringLiquibase liquibase = new SpringLiquibase();
....
return new CustomSpringLiquibase(liquibase);
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Radek Postołowicz |
| Solution 2 | Felix |
| Solution 3 | Moritz |
