Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrations performed by Flyway or Liquibase may not have completed before the database is accessed via jOOQ #25279

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -33,6 +33,7 @@
import org.flywaydb.core.api.callback.Callback;
import org.flywaydb.core.api.configuration.FluentConfiguration;
import org.flywaydb.core.api.migration.JavaMigration;
import org.jooq.DSLContext;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
Expand All @@ -44,6 +45,7 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.jpa.EntityManagerFactoryDependsOnPostProcessor;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration.FlywayDataSourceCondition;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration.FlywayDslContextDependsOnPostProcessor;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration.FlywayEntityManagerFactoryDependsOnPostProcessor;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration.FlywayJdbcOperationsDependsOnPostProcessor;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration.FlywayNamedParameterJdbcOperationsDependencyConfiguration;
Expand All @@ -52,6 +54,7 @@
import org.springframework.boot.autoconfigure.jdbc.JdbcOperationsDependsOnPostProcessor;
import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.NamedParameterJdbcOperationsDependsOnPostProcessor;
import org.springframework.boot.autoconfigure.jooq.DslContextDependsOnPostProcessor;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
Expand Down Expand Up @@ -96,7 +99,7 @@
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, JdbcTemplateAutoConfiguration.class,
HibernateJpaAutoConfiguration.class })
@Import({ FlywayEntityManagerFactoryDependsOnPostProcessor.class, FlywayJdbcOperationsDependsOnPostProcessor.class,
FlywayNamedParameterJdbcOperationsDependencyConfiguration.class })
FlywayNamedParameterJdbcOperationsDependencyConfiguration.class, FlywayDslContextDependsOnPostProcessor.class })
public class FlywayAutoConfiguration {

@Bean
Expand All @@ -115,7 +118,8 @@ public FlywaySchemaManagementProvider flywayDefaultDdlModeProvider(ObjectProvide
@EnableConfigurationProperties({ DataSourceProperties.class, FlywayProperties.class })
@Import({ FlywayMigrationInitializerEntityManagerFactoryDependsOnPostProcessor.class,
FlywayMigrationInitializerJdbcOperationsDependsOnPostProcessor.class,
FlywayMigrationInitializerNamedParameterJdbcOperationsDependsOnPostProcessor.class })
FlywayMigrationInitializerNamedParameterJdbcOperationsDependsOnPostProcessor.class,
FlywayMigrationInitializerDslContextDependsOnPostProcessor.class })
public static class FlywayConfiguration {

@Bean
Expand Down Expand Up @@ -326,6 +330,20 @@ static class FlywayMigrationInitializerNamedParameterJdbcOperationsDependsOnPost

}

/**
* Post processor to ensure that {@link DSLContext} beans depend on any
* {@link FlywayMigrationInitializer} beans.
*/
@ConditionalOnClass(DSLContext.class)
@ConditionalOnBean(DSLContext.class)
static class FlywayMigrationInitializerDslContextDependsOnPostProcessor extends DslContextDependsOnPostProcessor {

FlywayMigrationInitializerDslContextDependsOnPostProcessor() {
super(FlywayMigrationInitializer.class);
}

}

/**
* Post processor to ensure that {@link EntityManagerFactory} beans depend on any
* {@link Flyway} beans.
Expand Down Expand Up @@ -369,6 +387,20 @@ public FlywayNamedParameterJdbcOperationsDependencyConfiguration() {

}

/**
* Post processor to ensure that {@link DSLContext} beans depend on any {@link Flyway}
* beans.
*/
@ConditionalOnClass(DSLContext.class)
@ConditionalOnBean(DSLContext.class)
protected static class FlywayDslContextDependsOnPostProcessor extends DslContextDependsOnPostProcessor {

public FlywayDslContextDependsOnPostProcessor() {
super(Flyway.class);
}

}

private static class LocationResolver {

private static final String VENDOR_PLACEHOLDER = "{vendor}";
Expand Down
@@ -0,0 +1,53 @@
/*
* Copyright 2012-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.boot.autoconfigure.jooq;

import org.jooq.DSLContext;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.boot.autoconfigure.AbstractDependsOnBeanFactoryPostProcessor;

/**
* {@link BeanFactoryPostProcessor} that can be used to dynamically declare that all
* {@link DSLContext} beans should "depend on" one or more specific beans.
*
* @author Eddú Meléndez
* @since 2.3.9
* @see BeanDefinition#setDependsOn(String[])
*/
public class DslContextDependsOnPostProcessor extends AbstractDependsOnBeanFactoryPostProcessor {

/**
* Creates a new {@code DslContextDependsOnPostProcessor} that will set up
* dependencies upon beans with the given names.
* @param dependsOn names of the beans to depend upon
*/
public DslContextDependsOnPostProcessor(String... dependsOn) {
super(DSLContext.class, dependsOn);
}

/**
* Creates a new {@code DslContextDependsOnPostProcessor} that will set up
* dependencies upon beans with the given types.
* @param dependsOn types of the beans to depend upon
*/
public DslContextDependsOnPostProcessor(Class<?>... dependsOn) {
super(DSLContext.class, dependsOn);
}

}
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -23,6 +23,7 @@

import liquibase.change.DatabaseChange;
import liquibase.integration.spring.SpringLiquibase;
import org.jooq.DSLContext;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
Expand All @@ -37,7 +38,9 @@
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.jdbc.JdbcOperationsDependsOnPostProcessor;
import org.springframework.boot.autoconfigure.jdbc.NamedParameterJdbcOperationsDependsOnPostProcessor;
import org.springframework.boot.autoconfigure.jooq.DslContextDependsOnPostProcessor;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration.LiquibaseDataSourceCondition;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration.LiquibaseDslContextDependsOnPostProcessor;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration.LiquibaseEntityManagerFactoryDependsOnPostProcessor;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration.LiquibaseJdbcOperationsDependsOnPostProcessor;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration.LiquibaseNamedParameterJdbcOperationsDependsOnPostProcessor;
Expand Down Expand Up @@ -75,7 +78,8 @@
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
@Import({ LiquibaseEntityManagerFactoryDependsOnPostProcessor.class,
LiquibaseJdbcOperationsDependsOnPostProcessor.class,
LiquibaseNamedParameterJdbcOperationsDependsOnPostProcessor.class })
LiquibaseNamedParameterJdbcOperationsDependsOnPostProcessor.class,
LiquibaseDslContextDependsOnPostProcessor.class })
public class LiquibaseAutoConfiguration {

@Bean
Expand Down Expand Up @@ -206,6 +210,20 @@ static class LiquibaseNamedParameterJdbcOperationsDependsOnPostProcessor

}

/**
* Post processor to ensure that {@link DSLContext} beans depend on the liquibase
* bean.
*/
@ConditionalOnClass(DSLContext.class)
@ConditionalOnBean(DSLContext.class)
static class LiquibaseDslContextDependsOnPostProcessor extends DslContextDependsOnPostProcessor {

LiquibaseDslContextDependsOnPostProcessor() {
super(SpringLiquibase.class);
}

}

static final class LiquibaseDataSourceCondition extends AnyNestedCondition {

LiquibaseDataSourceCondition() {
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -32,11 +32,15 @@
import org.flywaydb.core.api.migration.JavaMigration;
import org.flywaydb.core.internal.license.FlywayProUpgradeRequiredException;
import org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform;
import org.jooq.DSLContext;
import org.jooq.SQLDialect;
import org.jooq.impl.DefaultDSLContext;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InOrder;

import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.boot.jdbc.DataSourceBuilder;
Expand Down Expand Up @@ -520,6 +524,25 @@ void initSqlsWithFlywayUrl() {
});
}

@Test
void userConfigurationDslContextDependency() {
this.contextRunner
.withUserConfiguration(EmbeddedDataSourceConfiguration.class, CustomFlywayWithJooqConfiguration.class)
.run((context) -> {
BeanDefinition beanDefinition = context.getBeanFactory().getBeanDefinition("dslContext");
assertThat(beanDefinition.getDependsOn()).containsExactly("flyway");
});
}

@Test
void userConfigurationWithFlywayMigrationAndDslContextDependency() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class,
CustomFlywayMigrationInitializerWithJooqConfiguration.class).run((context) -> {
BeanDefinition beanDefinition = context.getBeanFactory().getBeanDefinition("dslContext");
assertThat(beanDefinition.getDependsOn()).containsExactly("flywayMigrationInitializer", "flyway");
});
}

@Configuration(proxyBeanMethods = false)
static class FlywayDataSourceConfiguration {

Expand Down Expand Up @@ -748,6 +771,42 @@ FlywayConfigurationCustomizer customizerTwo() {

}

@Configuration
static class CustomFlywayWithJooqConfiguration {

@Bean
Flyway flyway(DataSource dataSource) {
return Flyway.configure().dataSource(dataSource).load();
}

@Bean
DSLContext dslContext() {
return new DefaultDSLContext(SQLDialect.H2);
}

}

@Configuration
protected static class CustomFlywayMigrationInitializerWithJooqConfiguration {

private final DataSource dataSource;

protected CustomFlywayMigrationInitializerWithJooqConfiguration(DataSource dataSource) {
this.dataSource = dataSource;
}

@Bean
public FlywayMigrationInitializer flywayMigrationInitializer(Flyway flyway) {
return new FlywayMigrationInitializer(flyway);
}

@Bean
DSLContext dslContext() {
return new DefaultDSLContext(SQLDialect.H2);
}

}

static final class CustomClassLoader extends ClassLoader {

private CustomClassLoader(ClassLoader parent) {
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -41,6 +41,7 @@
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
import org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.context.event.ApplicationStartingEvent;
import org.springframework.boot.jdbc.DataSourceBuilder;
Expand Down Expand Up @@ -374,6 +375,16 @@ void overrideTag() {
.run(assertLiquibase((liquibase) -> assertThat(liquibase.getTag()).isEqualTo("1.0.0")));
}

@Test
void userConfigurationDslContextDependency() {
this.contextRunner.withConfiguration(AutoConfigurations.of(JooqAutoConfiguration.class))
.withUserConfiguration(LiquibaseUserConfiguration.class, EmbeddedDataSourceConfiguration.class)
.run((context) -> {
BeanDefinition beanDefinition = context.getBeanFactory().getBeanDefinition("dslContext");
assertThat(beanDefinition.getDependsOn()).containsExactly("springLiquibase");
});
}

private ContextConsumer<AssertableApplicationContext> assertLiquibase(Consumer<SpringLiquibase> consumer) {
return (context) -> {
assertThat(context).hasSingleBean(SpringLiquibase.class);
Expand Down