I know that this is a circular dependency problem: JPA is waiting for liquibase and customchange set from liquibase is waiting for JPA. With <= 2.4.x there was no hard dependency between each other. It is possible to get old behavior back?

We use a similar solution to get spring context inside Liquibase: https://stackoverflow.com/a/64922642/218449 (Solution B) or in our case: https://stackoverflow.com/a/33041431/218449 (AutowireHelper is executed during Liquibase CustomChangeSet Setup)

Now we get the following error.

 liquibase.exception.UnexpectedLiquibaseException: liquibase.exception.CustomChangeException:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'MyCustomChangeSetWithSpringBeans': Unsatisfied dependency expressed through field 'txTemplate'; 
nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'txTemplateDefault' defined in class path resource [DatabaseConfiguration.class]: Unsatisfied dependency expressed through method 'txTemplate' parameter 0; 
nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManager' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Initialization of bean failed; 
nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'liquibase': Requested bean is currently in creation: Is there an unresolvable circular reference?

I can create a small example if required

The requirement is simple: It is a lot easier to use JPA entities/sessions/repositories to solve complex migration use cases and to reuse business code (for entity-transformations)

Current workaround: Disable LiquibaseAutoConfiguration and create no bean of type SpringLiquibase. Instead I use composition with a custom bean and this bean is initialized near the end of the startup phase (after JPA/tx-manager).

    @Bean
    @DependsOn({ "autowireHelper", "txTemplateDefault" })
    public SpringLiquibaseWatch liquibase(LiquibaseProperties liquibaseProperties, DataSource defaultDataSource) throws LiquibaseException {
        SpringLiquibase liquibase = new SpringLiquibase();
        liquibase.setDataSource(liquibaseDataSource(liquibaseProperties, defaultDataSource));
        liquibase.setChangeLog(liquibaseProperties.getChangeLog());
        liquibase.setContexts(liquibaseProperties.getContexts());
        liquibase.setDefaultSchema(liquibaseProperties.getDefaultSchema());
        liquibase.setDropFirst(liquibaseProperties.isDropFirst());
        liquibase.setShouldRun(liquibaseProperties.isEnabled());
        log.debug("Configuring Liquibase (Context: {})", liquibaseProperties.getContexts());
        return new SpringLiquibaseWatch(liquibase);
    }
    ....

custom class:

public class SpringLiquibaseWatch implements InitializingBean, BeanNameAware, ResourceLoaderAware {

    private SpringLiquibase springLiquibase;

    public void afterPropertiesSet() throws LiquibaseException {
        log.debug("Starting Liquibase synchronously");
        initDb();
    }

    protected void initDb() throws LiquibaseException {
        StopWatch watch = new StopWatch();
        watch.start();
        springLiquibase.afterPropertiesSet();
        watch.stop();
        log.debug("Started Liquibase in {} ms", watch.getTotalTimeMillis());
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        springLiquibase.setResourceLoader(resourceLoader);
    }

    @Override
    public void setBeanName(String name) {
        springLiquibase.setBeanName(name);
    }
}

Comment From: wilkinsona

Apologies for the unexpected change in behaviour. Unfortunately for your needs, the change is unexpected as, from what you have described, this should not work in 2.4 or earlier either.

The mechanism that sets up the dependencies between beans has changed in 2.5, but the behaviour should be the same. Any EntityManagerFactory beans should be configured to depend upon any SpringLiquibase beans. This is to ensure that Liquibase has migrated the database before it’s accessed via JPA.

It sounds like that is not working or its configuration has backed off when you use 2.4 and I’d like to understand why. Until we have that understanding we will not be in a position to consider what, if anything, we want to do in 2.5. Can you please provide a minimal sample, either zipped up and attached to this issue or in a separate repository on GitHub, that works with 2.4 and fails with 2.5?

Comment From: gbrehmer

I was not able to reproduce the issue with a simple demo project: https://github.com/gbrehmer/demo-spring-boot-27231 Can you see the cause, why JPA is fully initialized before liquibase w/o our workaround? In addition also junit tests are working fine w/o the workaround. Of course text context contains some mocks and H2 instaed of PostgreSQL, but the liquibase changesets are nearly the same

Comment From: wilkinsona

Thanks for the sample.

When there's a circular reference between beans, Framework tries very hard to allow refresh of the application context to continue. In your sample it has succeeded but in your real application it doesn't. Unfortunately, I can't tell why it would fail in your real application without seeing it do so.

If you enable trace level logging for org.springframework.beans.factory.support when running your sample you will see the bean factory indicate that it is returning a partially initialized liquibase bean to break the cycle:

2021-07-11 17:13:55.585 TRACE 83474 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Returning eagerly cached instance of singleton bean 'liquibase' that is not fully initialized yet - a consequence of a circular reference

In general terms, this is a bad thing for database initialization as it means that the entity manager could be used before Liquibase has prepared the database. I'd like to see if we can fix this in 2.5 so that the attempt to break the cycle isn't made and it fails consistently in this sort of situation.

In 2.4, I suspect there's something about the dependencies between beans and how they're expressed that allows the bean factory to break the cycle well enough for your purposes. In other words, it behaves similarly to how your 2.5-based sample behaves. Database initialization is extremely complicated in 2.4 and earlier so I think we'll live with this behaviour there until it becomes a problem for someone.

For your specific use case, I don't yet know what we should do and I'd like to discuss it with the rest of the team. This has never worked by design so, just to set expectations, it's possible that we will decide that we don't want to complicate things in 2.5 by trying to support it. Given that you have a workaround, hopefully that won't cause too much inconvenience.

Comment From: gbrehmer

Thanks for your detailed explanations. I will check the trace logs next week. I can understand that a consistent and easy-to-understand approach is preferable. From our perspective, the use of JPA in the schema migrations has great advantages for us, but in the end it also has clear disadvantages: In addition to this problem, an entity only represents one state of the DB (the current one) and thus cannot necessarily be used in older migrations (potentially multiple versions of an entity need to be created that contain different mapping states). Nevertheless, the consistent interface to the database (JPA, entities, repositories) prevails for us. Potentially, one would have to create a separate embedded Spring context for Liquibase.

Comment From: wilkinsona

We discussed this today and decided that this isn't something that we want to support. We'd recommend that you continue with your workaround or use a child context to break the dependency cycle.