Reinstate support for jOOQ as it now supports Jakarta EE 9

Closes gh-29271
This commit is contained in:
Andy Wilkinson
2022-01-05 15:46:54 +00:00
parent ccc924b908
commit c2e86096cd
46 changed files with 2201 additions and 2 deletions

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2012-2022 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.impl.DefaultConfiguration;
/**
* Callback interface that can be implemented by beans wishing to customize the
* {@link DefaultConfiguration} whilst retaining default auto-configuration.
*
* @author Stephane Nicoll
* @since 2.5.0
*/
@FunctionalInterface
public interface DefaultConfigurationCustomizer {
/**
* Customize the {@link DefaultConfiguration jOOQ Configuration}.
* @param configuration the configuration to customize
*/
void customize(DefaultConfiguration configuration);
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2012-2022 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;
import org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector;
/**
* {@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[])
* @deprecated since 2.5.0 for removal in 2.7.0 in favor of
* {@link DependsOnDatabaseInitializationDetector}
*/
@Deprecated
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);
}
}

View File

@@ -0,0 +1,154 @@
/*
* Copyright 2012-2022 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 javax.sql.DataSource;
import org.jooq.ConnectionProvider;
import org.jooq.DSLContext;
import org.jooq.ExecuteListenerProvider;
import org.jooq.ExecutorProvider;
import org.jooq.RecordListenerProvider;
import org.jooq.RecordMapperProvider;
import org.jooq.RecordUnmapperProvider;
import org.jooq.TransactionListenerProvider;
import org.jooq.TransactionProvider;
import org.jooq.VisitListenerProvider;
import org.jooq.conf.Settings;
import org.jooq.impl.DataSourceConnectionProvider;
import org.jooq.impl.DefaultConfiguration;
import org.jooq.impl.DefaultDSLContext;
import org.jooq.impl.DefaultExecuteListenerProvider;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import org.springframework.transaction.PlatformTransactionManager;
/**
* {@link EnableAutoConfiguration Auto-configuration} for JOOQ.
*
* @author Andreas Ahlenstorf
* @author Michael Simons
* @author Dmytro Nosan
* @since 1.3.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(DSLContext.class)
@ConditionalOnBean(DataSource.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, TransactionAutoConfiguration.class })
public class JooqAutoConfiguration {
@Bean
@ConditionalOnMissingBean(ConnectionProvider.class)
public DataSourceConnectionProvider dataSourceConnectionProvider(DataSource dataSource) {
return new DataSourceConnectionProvider(new TransactionAwareDataSourceProxy(dataSource));
}
@Bean
@ConditionalOnBean(PlatformTransactionManager.class)
public SpringTransactionProvider transactionProvider(PlatformTransactionManager txManager) {
return new SpringTransactionProvider(txManager);
}
@Bean
@Order(0)
public DefaultExecuteListenerProvider jooqExceptionTranslatorExecuteListenerProvider() {
return new DefaultExecuteListenerProvider(new JooqExceptionTranslator());
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(DSLContext.class)
@EnableConfigurationProperties(JooqProperties.class)
public static class DslContextConfiguration {
@Bean
public DefaultDSLContext dslContext(org.jooq.Configuration configuration) {
return new DefaultDSLContext(configuration);
}
@Bean
@ConditionalOnMissingBean(org.jooq.Configuration.class)
public DefaultConfiguration jooqConfiguration(JooqProperties properties, ConnectionProvider connectionProvider,
DataSource dataSource, ObjectProvider<ExecuteListenerProvider> executeListenerProviders,
ObjectProvider<DefaultConfigurationCustomizer> configurationCustomizers) {
DefaultConfiguration configuration = new DefaultConfiguration();
configuration.set(properties.determineSqlDialect(dataSource));
configuration.set(connectionProvider);
configuration.set(executeListenerProviders.orderedStream().toArray(ExecuteListenerProvider[]::new));
configurationCustomizers.orderedStream().forEach((customizer) -> customizer.customize(configuration));
return configuration;
}
@Bean
@Deprecated
public DefaultConfigurationCustomizer jooqProvidersDefaultConfigurationCustomizer(
ObjectProvider<TransactionProvider> transactionProvider,
ObjectProvider<RecordMapperProvider> recordMapperProvider,
ObjectProvider<RecordUnmapperProvider> recordUnmapperProvider, ObjectProvider<Settings> settings,
ObjectProvider<RecordListenerProvider> recordListenerProviders,
ObjectProvider<VisitListenerProvider> visitListenerProviders,
ObjectProvider<TransactionListenerProvider> transactionListenerProviders,
ObjectProvider<ExecutorProvider> executorProvider) {
return new OrderedDefaultConfigurationCustomizer((configuration) -> {
transactionProvider.ifAvailable(configuration::set);
recordMapperProvider.ifAvailable(configuration::set);
recordUnmapperProvider.ifAvailable(configuration::set);
settings.ifAvailable(configuration::set);
executorProvider.ifAvailable(configuration::set);
configuration.set(recordListenerProviders.orderedStream().toArray(RecordListenerProvider[]::new));
configuration.set(visitListenerProviders.orderedStream().toArray(VisitListenerProvider[]::new));
configuration.setTransactionListenerProvider(
transactionListenerProviders.orderedStream().toArray(TransactionListenerProvider[]::new));
});
}
}
private static class OrderedDefaultConfigurationCustomizer implements DefaultConfigurationCustomizer, Ordered {
private final DefaultConfigurationCustomizer delegate;
OrderedDefaultConfigurationCustomizer(DefaultConfigurationCustomizer delegate) {
this.delegate = delegate;
}
@Override
public void customize(DefaultConfiguration configuration) {
this.delegate.customize(configuration);
}
@Override
public int getOrder() {
return 0;
}
}
}

View File

@@ -0,0 +1,97 @@
/*
* Copyright 2012-2022 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 java.sql.SQLException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jooq.ExecuteContext;
import org.jooq.SQLDialect;
import org.jooq.impl.DefaultExecuteListener;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator;
import org.springframework.jdbc.support.SQLExceptionTranslator;
import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
/**
* Transforms {@link java.sql.SQLException} into a Spring-specific
* {@link DataAccessException}.
*
* @author Lukas Eder
* @author Andreas Ahlenstorf
* @author Phillip Webb
* @author Stephane Nicoll
* @since 1.5.10
*/
public class JooqExceptionTranslator extends DefaultExecuteListener {
// Based on the jOOQ-spring-example from https://github.com/jOOQ/jOOQ
private static final Log logger = LogFactory.getLog(JooqExceptionTranslator.class);
@Override
public void exception(ExecuteContext context) {
SQLExceptionTranslator translator = getTranslator(context);
// The exception() callback is not only triggered for SQL exceptions but also for
// "normal" exceptions. In those cases sqlException() returns null.
SQLException exception = context.sqlException();
while (exception != null) {
handle(context, translator, exception);
exception = exception.getNextException();
}
}
private SQLExceptionTranslator getTranslator(ExecuteContext context) {
SQLDialect dialect = context.configuration().dialect();
if (dialect != null && dialect.thirdParty() != null) {
String dbName = dialect.thirdParty().springDbName();
if (dbName != null) {
return new SQLErrorCodeSQLExceptionTranslator(dbName);
}
}
return new SQLStateSQLExceptionTranslator();
}
/**
* Handle a single exception in the chain. SQLExceptions might be nested multiple
* levels deep. The outermost exception is usually the least interesting one ("Call
* getNextException to see the cause."). Therefore the innermost exception is
* propagated and all other exceptions are logged.
* @param context the execute context
* @param translator the exception translator
* @param exception the exception
*/
private void handle(ExecuteContext context, SQLExceptionTranslator translator, SQLException exception) {
DataAccessException translated = translate(context, translator, exception);
if (exception.getNextException() == null) {
if (translated != null) {
context.exception(translated);
}
}
else {
logger.error("Execution of SQL statement failed.", (translated != null) ? translated : exception);
}
}
private DataAccessException translate(ExecuteContext context, SQLExceptionTranslator translator,
SQLException exception) {
return translator.translate("jOOQ", context.sql(), exception);
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2012-2022 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 javax.sql.DataSource;
import org.jooq.SQLDialect;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Configuration properties for the JOOQ database library.
*
* @author Andreas Ahlenstorf
* @author Michael Simons
* @since 1.3.0
*/
@ConfigurationProperties(prefix = "spring.jooq")
public class JooqProperties {
/**
* SQL dialect to use. Auto-detected by default.
*/
private SQLDialect sqlDialect;
public SQLDialect getSqlDialect() {
return this.sqlDialect;
}
public void setSqlDialect(SQLDialect sqlDialect) {
this.sqlDialect = sqlDialect;
}
/**
* Determine the {@link SQLDialect} to use based on this configuration and the primary
* {@link DataSource}.
* @param dataSource the data source
* @return the {@code SQLDialect} to use for that {@link DataSource}
*/
public SQLDialect determineSqlDialect(DataSource dataSource) {
if (this.sqlDialect != null) {
return this.sqlDialect;
}
return SqlDialectLookup.getDialect(dataSource);
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright 2012-2022 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.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;
import org.springframework.core.Ordered;
class NoDslContextBeanFailureAnalyzer extends AbstractFailureAnalyzer<NoSuchBeanDefinitionException>
implements Ordered, BeanFactoryAware {
private BeanFactory beanFactory;
@Override
protected FailureAnalysis analyze(Throwable rootFailure, NoSuchBeanDefinitionException cause) {
if (DSLContext.class.equals(cause.getBeanType()) && hasR2dbcAutoConfiguration()) {
return new FailureAnalysis(
"jOOQ has not been auto-configured as R2DBC has been auto-configured in favor of JDBC and jOOQ "
+ "auto-configuration does not yet support R2DBC. ",
"To use jOOQ with JDBC, exclude R2dbcAutoConfiguration. To use jOOQ with R2DBC, define your own "
+ "jOOQ configuration.",
cause);
}
return null;
}
private boolean hasR2dbcAutoConfiguration() {
try {
this.beanFactory.getBean(R2dbcAutoConfiguration.class);
return true;
}
catch (Exception ex) {
return false;
}
}
@Override
public int getOrder() {
return 0;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2012-2022 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.Transaction;
import org.springframework.transaction.TransactionStatus;
/**
* Adapts a Spring transaction for JOOQ.
*
* @author Lukas Eder
* @author Andreas Ahlenstorf
* @author Phillip Webb
*/
class SpringTransaction implements Transaction {
// Based on the jOOQ-spring-example from https://github.com/jOOQ/jOOQ
private final TransactionStatus transactionStatus;
SpringTransaction(TransactionStatus transactionStatus) {
this.transactionStatus = transactionStatus;
}
TransactionStatus getTxStatus() {
return this.transactionStatus;
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2012-2022 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.TransactionContext;
import org.jooq.TransactionProvider;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
/**
* Allows Spring Transaction to be used with JOOQ.
*
* @author Lukas Eder
* @author Andreas Ahlenstorf
* @author Phillip Webb
* @since 1.5.10
*/
public class SpringTransactionProvider implements TransactionProvider {
// Based on the jOOQ-spring-example from https://github.com/jOOQ/jOOQ
private final PlatformTransactionManager transactionManager;
public SpringTransactionProvider(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
@Override
public void begin(TransactionContext context) {
TransactionDefinition definition = new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_NESTED);
TransactionStatus status = this.transactionManager.getTransaction(definition);
context.transaction(new SpringTransaction(status));
}
@Override
public void commit(TransactionContext ctx) {
this.transactionManager.commit(getTransactionStatus(ctx));
}
@Override
public void rollback(TransactionContext ctx) {
this.transactionManager.rollback(getTransactionStatus(ctx));
}
private TransactionStatus getTransactionStatus(TransactionContext ctx) {
SpringTransaction transaction = (SpringTransaction) ctx.transaction();
return transaction.getTxStatus();
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2012-2022 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 java.sql.DatabaseMetaData;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jooq.SQLDialect;
import org.jooq.tools.jdbc.JDBCUtils;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.jdbc.support.MetaDataAccessException;
/**
* Utility to lookup well known {@link SQLDialect SQLDialects} from a {@link DataSource}.
*
* @author Michael Simons
* @author Lukas Eder
*/
final class SqlDialectLookup {
private static final Log logger = LogFactory.getLog(SqlDialectLookup.class);
private SqlDialectLookup() {
}
/**
* Return the most suitable {@link SQLDialect} for the given {@link DataSource}.
* @param dataSource the source {@link DataSource}
* @return the most suitable {@link SQLDialect}
*/
static SQLDialect getDialect(DataSource dataSource) {
if (dataSource == null) {
return SQLDialect.DEFAULT;
}
try {
String url = JdbcUtils.extractDatabaseMetaData(dataSource, DatabaseMetaData::getURL);
SQLDialect sqlDialect = JDBCUtils.dialect(url);
if (sqlDialect != null) {
return sqlDialect;
}
}
catch (MetaDataAccessException ex) {
logger.warn("Unable to determine jdbc url from datasource", ex);
}
return SQLDialect.DEFAULT;
}
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2012-2022 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.
*/
/**
* Auto-configuration for JOOQ.
*/
package org.springframework.boot.autoconfigure.jooq;

View File

@@ -87,6 +87,7 @@ org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConf
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration,\
@@ -158,6 +159,7 @@ org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinition
org.springframework.boot.autoconfigure.flyway.FlywayMigrationScriptMissingFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer,\
org.springframework.boot.autoconfigure.jooq.NoDslContextBeanFailureAnalyzer,\
org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryBeanCreationFailureAnalyzer,\
org.springframework.boot.autoconfigure.r2dbc.MissingR2dbcPoolDependencyFailureAnalyzer,\
org.springframework.boot.autoconfigure.r2dbc.MultipleConnectionPoolConfigurationsFailureAnalzyer,\

View File

@@ -36,11 +36,15 @@ import org.flywaydb.core.internal.license.FlywayTeamsUpgradeRequiredException;
import org.flywaydb.core.internal.plugin.PluginRegister;
import org.flywaydb.database.sqlserver.SQLServerConfigurationExtension;
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.DataSourceProperties;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
@@ -649,6 +653,34 @@ class FlywayAutoConfigurationTests {
.run(validateFlywayTeamsPropertyOnly("skipExecutingMigrations"));
}
@Test
void whenFlywayIsAutoConfiguredThenJooqDslContextDependsOnFlywayBeans() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, JooqConfiguration.class)
.run((context) -> {
BeanDefinition beanDefinition = context.getBeanFactory().getBeanDefinition("dslContext");
assertThat(beanDefinition.getDependsOn()).containsExactlyInAnyOrder("flywayInitializer", "flyway");
});
}
@Test
void whenCustomMigrationInitializerIsDefinedThenJooqDslContextDependsOnIt() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, JooqConfiguration.class,
CustomFlywayMigrationInitializer.class).run((context) -> {
BeanDefinition beanDefinition = context.getBeanFactory().getBeanDefinition("dslContext");
assertThat(beanDefinition.getDependsOn()).containsExactlyInAnyOrder("flywayMigrationInitializer",
"flyway");
});
}
@Test
void whenCustomFlywayIsDefinedThenJooqDslContextDependsOnIt() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, JooqConfiguration.class,
CustomFlyway.class).run((context) -> {
BeanDefinition beanDefinition = context.getBeanFactory().getBeanDefinition("dslContext");
assertThat(beanDefinition.getDependsOn()).containsExactlyInAnyOrder("customFlyway");
});
}
@Test
void baselineMigrationPrefixIsCorrectlyMapped() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
@@ -920,6 +952,16 @@ class FlywayAutoConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
static class JooqConfiguration {
@Bean
DSLContext dslContext() {
return new DefaultDSLContext(SQLDialect.H2);
}
}
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(DataSourceProperties.class)
abstract static class AbstractUserH2DataSourceConfiguration {

View File

@@ -0,0 +1,262 @@
/*
* Copyright 2012-2022 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 javax.sql.DataSource;
import org.jooq.CharsetProvider;
import org.jooq.ConnectionProvider;
import org.jooq.ConverterProvider;
import org.jooq.DSLContext;
import org.jooq.ExecuteListener;
import org.jooq.ExecuteListenerProvider;
import org.jooq.ExecutorProvider;
import org.jooq.RecordListenerProvider;
import org.jooq.RecordMapperProvider;
import org.jooq.RecordUnmapperProvider;
import org.jooq.SQLDialect;
import org.jooq.TransactionListenerProvider;
import org.jooq.TransactionalRunnable;
import org.jooq.VisitListenerProvider;
import org.jooq.impl.DataSourceConnectionProvider;
import org.jooq.impl.DefaultExecuteListenerProvider;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import org.springframework.transaction.PlatformTransactionManager;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link JooqAutoConfiguration}.
*
* @author Andreas Ahlenstorf
* @author Phillip Webb
* @author Andy Wilkinson
* @author Stephane Nicoll
* @author Dmytro Nosan
*/
class JooqAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(JooqAutoConfiguration.class))
.withPropertyValues("spring.datasource.name:jooqtest");
@Test
void noDataSource() {
this.contextRunner.run((context) -> assertThat(context.getBeansOfType(DSLContext.class)).isEmpty());
}
@Test
void jooqWithoutTx() {
this.contextRunner.withUserConfiguration(JooqDataSourceConfiguration.class).run((context) -> {
assertThat(context).doesNotHaveBean(PlatformTransactionManager.class);
assertThat(context).doesNotHaveBean(SpringTransactionProvider.class);
DSLContext dsl = context.getBean(DSLContext.class);
dsl.execute("create table jooqtest (name varchar(255) primary key);");
dsl.transaction(new AssertFetch(dsl, "select count(*) as total from jooqtest;", "0"));
dsl.transaction(new ExecuteSql(dsl, "insert into jooqtest (name) values ('foo');"));
dsl.transaction(new AssertFetch(dsl, "select count(*) as total from jooqtest;", "1"));
assertThatExceptionOfType(DataIntegrityViolationException.class)
.isThrownBy(() -> dsl.transaction(new ExecuteSql(dsl, "insert into jooqtest (name) values ('bar');",
"insert into jooqtest (name) values ('foo');")));
dsl.transaction(new AssertFetch(dsl, "select count(*) as total from jooqtest;", "2"));
});
}
@Test
void jooqWithTx() {
this.contextRunner.withUserConfiguration(JooqDataSourceConfiguration.class, TxManagerConfiguration.class)
.run((context) -> {
assertThat(context).hasSingleBean(PlatformTransactionManager.class);
DSLContext dsl = context.getBean(DSLContext.class);
assertThat(dsl.configuration().dialect()).isEqualTo(SQLDialect.HSQLDB);
dsl.execute("create table jooqtest_tx (name varchar(255) primary key);");
dsl.transaction(new AssertFetch(dsl, "select count(*) as total from jooqtest_tx;", "0"));
dsl.transaction(new ExecuteSql(dsl, "insert into jooqtest_tx (name) values ('foo');"));
dsl.transaction(new AssertFetch(dsl, "select count(*) as total from jooqtest_tx;", "1"));
assertThatExceptionOfType(DataIntegrityViolationException.class).isThrownBy(
() -> dsl.transaction(new ExecuteSql(dsl, "insert into jooqtest (name) values ('bar');",
"insert into jooqtest (name) values ('foo');")));
dsl.transaction(new AssertFetch(dsl, "select count(*) as total from jooqtest_tx;", "1"));
});
}
@Test
void jooqWithDefaultConnectionProvider() {
this.contextRunner.withUserConfiguration(JooqDataSourceConfiguration.class).run((context) -> {
DSLContext dsl = context.getBean(DSLContext.class);
ConnectionProvider connectionProvider = dsl.configuration().connectionProvider();
assertThat(connectionProvider).isInstanceOf(DataSourceConnectionProvider.class);
DataSource connectionProviderDataSource = ((DataSourceConnectionProvider) connectionProvider).dataSource();
assertThat(connectionProviderDataSource).isInstanceOf(TransactionAwareDataSourceProxy.class);
});
}
@Test
void jooqWithDefaultExecuteListenerProvider() {
this.contextRunner.withUserConfiguration(JooqDataSourceConfiguration.class).run((context) -> {
DSLContext dsl = context.getBean(DSLContext.class);
assertThat(dsl.configuration().executeListenerProviders()).hasSize(1);
});
}
@Test
void jooqWithSeveralExecuteListenerProviders() {
this.contextRunner.withUserConfiguration(JooqDataSourceConfiguration.class, TestExecuteListenerProvider.class)
.run((context) -> {
DSLContext dsl = context.getBean(DSLContext.class);
ExecuteListenerProvider[] executeListenerProviders = dsl.configuration().executeListenerProviders();
assertThat(executeListenerProviders).hasSize(2);
assertThat(executeListenerProviders[0]).isInstanceOf(DefaultExecuteListenerProvider.class);
assertThat(executeListenerProviders[1]).isInstanceOf(TestExecuteListenerProvider.class);
});
}
@Test
void dslContextWithConfigurationCustomizersAreApplied() {
ConverterProvider converterProvider = mock(ConverterProvider.class);
CharsetProvider charsetProvider = mock(CharsetProvider.class);
this.contextRunner.withUserConfiguration(JooqDataSourceConfiguration.class)
.withBean("configurationCustomizer1", DefaultConfigurationCustomizer.class,
() -> (configuration) -> configuration.set(converterProvider))
.withBean("configurationCustomizer2", DefaultConfigurationCustomizer.class,
() -> (configuration) -> configuration.set(charsetProvider))
.run((context) -> {
DSLContext dsl = context.getBean(DSLContext.class);
assertThat(dsl.configuration().converterProvider()).isSameAs(converterProvider);
assertThat(dsl.configuration().charsetProvider()).isSameAs(charsetProvider);
});
}
@Test
@Deprecated
void customProvidersArePickedUp() {
RecordMapperProvider recordMapperProvider = mock(RecordMapperProvider.class);
RecordUnmapperProvider recordUnmapperProvider = mock(RecordUnmapperProvider.class);
RecordListenerProvider recordListenerProvider = mock(RecordListenerProvider.class);
VisitListenerProvider visitListenerProvider = mock(VisitListenerProvider.class);
TransactionListenerProvider transactionListenerProvider = mock(TransactionListenerProvider.class);
ExecutorProvider executorProvider = mock(ExecutorProvider.class);
this.contextRunner.withUserConfiguration(JooqDataSourceConfiguration.class, TxManagerConfiguration.class)
.withBean(RecordMapperProvider.class, () -> recordMapperProvider)
.withBean(RecordUnmapperProvider.class, () -> recordUnmapperProvider)
.withBean(RecordListenerProvider.class, () -> recordListenerProvider)
.withBean(VisitListenerProvider.class, () -> visitListenerProvider)
.withBean(TransactionListenerProvider.class, () -> transactionListenerProvider)
.withBean(ExecutorProvider.class, () -> executorProvider).run((context) -> {
DSLContext dsl = context.getBean(DSLContext.class);
assertThat(dsl.configuration().recordMapperProvider()).isSameAs(recordMapperProvider);
assertThat(dsl.configuration().recordUnmapperProvider()).isSameAs(recordUnmapperProvider);
assertThat(dsl.configuration().executorProvider()).isSameAs(executorProvider);
assertThat(dsl.configuration().recordListenerProviders()).containsExactly(recordListenerProvider);
assertThat(dsl.configuration().visitListenerProviders()).containsExactly(visitListenerProvider);
assertThat(dsl.configuration().transactionListenerProviders())
.containsExactly(transactionListenerProvider);
});
}
@Test
void relaxedBindingOfSqlDialect() {
this.contextRunner.withUserConfiguration(JooqDataSourceConfiguration.class)
.withPropertyValues("spring.jooq.sql-dialect:PoSTGrES")
.run((context) -> assertThat(context.getBean(org.jooq.Configuration.class).dialect())
.isEqualTo(SQLDialect.POSTGRES));
}
static class AssertFetch implements TransactionalRunnable {
private final DSLContext dsl;
private final String sql;
private final String expected;
AssertFetch(DSLContext dsl, String sql, String expected) {
this.dsl = dsl;
this.sql = sql;
this.expected = expected;
}
@Override
public void run(org.jooq.Configuration configuration) {
assertThat(this.dsl.fetch(this.sql).getValue(0, 0).toString()).isEqualTo(this.expected);
}
}
static class ExecuteSql implements TransactionalRunnable {
private final DSLContext dsl;
private final String[] sql;
ExecuteSql(DSLContext dsl, String... sql) {
this.dsl = dsl;
this.sql = sql;
}
@Override
public void run(org.jooq.Configuration configuration) {
for (String statement : this.sql) {
this.dsl.execute(statement);
}
}
}
@Configuration(proxyBeanMethods = false)
static class JooqDataSourceConfiguration {
@Bean
DataSource jooqDataSource() {
return DataSourceBuilder.create().url("jdbc:hsqldb:mem:jooqtest").username("sa").build();
}
}
@Configuration(proxyBeanMethods = false)
static class TxManagerConfiguration {
@Bean
PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
@Order(100)
static class TestExecuteListenerProvider implements ExecuteListenerProvider {
@Override
public ExecuteListener provide() {
return null;
}
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright 2012-2022 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 java.sql.SQLException;
import org.jooq.Configuration;
import org.jooq.ExecuteContext;
import org.jooq.SQLDialect;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.ArgumentCaptor;
import org.springframework.jdbc.BadSqlGrammarException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link JooqExceptionTranslator}
*
* @author Andy Wilkinson
*/
class JooqExceptionTranslatorTests {
private final JooqExceptionTranslator exceptionTranslator = new JooqExceptionTranslator();
@ParameterizedTest(name = "{0}")
@MethodSource
void exceptionTranslation(SQLDialect dialect, SQLException sqlException) {
ExecuteContext context = mock(ExecuteContext.class);
Configuration configuration = mock(Configuration.class);
given(context.configuration()).willReturn(configuration);
given(configuration.dialect()).willReturn(dialect);
given(context.sqlException()).willReturn(sqlException);
this.exceptionTranslator.exception(context);
ArgumentCaptor<RuntimeException> captor = ArgumentCaptor.forClass(RuntimeException.class);
verify(context).exception(captor.capture());
assertThat(captor.getValue()).isInstanceOf(BadSqlGrammarException.class);
}
@Test
void whenExceptionCannotBeTranslatedThenExecuteContextExceptionIsNotCalled() {
ExecuteContext context = mock(ExecuteContext.class);
Configuration configuration = mock(Configuration.class);
given(context.configuration()).willReturn(configuration);
given(configuration.dialect()).willReturn(SQLDialect.POSTGRES);
given(context.sqlException()).willReturn(new SQLException(null, null, 123456789));
this.exceptionTranslator.exception(context);
verify(context, times(0)).exception(any());
}
static Object[] exceptionTranslation() {
return new Object[] { new Object[] { SQLDialect.DERBY, sqlException("42802") },
new Object[] { SQLDialect.H2, sqlException(42000) },
new Object[] { SQLDialect.HSQLDB, sqlException(-22) },
new Object[] { SQLDialect.MARIADB, sqlException(1054) },
new Object[] { SQLDialect.MYSQL, sqlException(1054) },
new Object[] { SQLDialect.POSTGRES, sqlException("03000") },
new Object[] { SQLDialect.SQLITE, sqlException("21000") } };
}
private static SQLException sqlException(String sqlState) {
return new SQLException(null, sqlState);
}
private static SQLException sqlException(int vendorCode) {
return new SQLException(null, null, vendorCode);
}
}

View File

@@ -0,0 +1,122 @@
/*
* Copyright 2012-2022 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 java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.jooq.SQLDialect;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link JooqProperties}.
*
* @author Stephane Nicoll
*/
class JooqPropertiesTests {
private AnnotationConfigApplicationContext context;
@AfterEach
void close() {
if (this.context != null) {
this.context.close();
}
}
@Test
void determineSqlDialectNoCheckIfDialectIsSet() throws SQLException {
JooqProperties properties = load("spring.jooq.sql-dialect=postgres");
DataSource dataSource = mockStandaloneDataSource();
SQLDialect sqlDialect = properties.determineSqlDialect(dataSource);
assertThat(sqlDialect).isEqualTo(SQLDialect.POSTGRES);
verify(dataSource, never()).getConnection();
}
@Test
void determineSqlDialectWithKnownUrl() {
JooqProperties properties = load();
SQLDialect sqlDialect = properties.determineSqlDialect(mockDataSource("jdbc:h2:mem:testdb"));
assertThat(sqlDialect).isEqualTo(SQLDialect.H2);
}
@Test
void determineSqlDialectWithKnownUrlAndUserConfig() {
JooqProperties properties = load("spring.jooq.sql-dialect=mysql");
SQLDialect sqlDialect = properties.determineSqlDialect(mockDataSource("jdbc:h2:mem:testdb"));
assertThat(sqlDialect).isEqualTo(SQLDialect.MYSQL);
}
@Test
void determineSqlDialectWithUnknownUrl() {
JooqProperties properties = load();
SQLDialect sqlDialect = properties.determineSqlDialect(mockDataSource("jdbc:unknown://localhost"));
assertThat(sqlDialect).isEqualTo(SQLDialect.DEFAULT);
}
private DataSource mockStandaloneDataSource() throws SQLException {
DataSource ds = mock(DataSource.class);
given(ds.getConnection()).willThrow(SQLException.class);
return ds;
}
private DataSource mockDataSource(String jdbcUrl) {
DataSource ds = mock(DataSource.class);
try {
DatabaseMetaData metadata = mock(DatabaseMetaData.class);
given(metadata.getURL()).willReturn(jdbcUrl);
Connection connection = mock(Connection.class);
given(connection.getMetaData()).willReturn(metadata);
given(ds.getConnection()).willReturn(connection);
}
catch (SQLException ex) {
// Do nothing
}
return ds;
}
private JooqProperties load(String... environment) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
TestPropertyValues.of(environment).applyTo(ctx);
ctx.register(TestConfiguration.class);
ctx.refresh();
this.context = ctx;
return this.context.getBean(JooqProperties.class);
}
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(JooqProperties.class)
static class TestConfiguration {
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2012-2022 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.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link NoDslContextBeanFailureAnalyzer}.
*
* @author Andy Wilkinson
*/
class NoDslContextBeanFailureAnalyzerTests {
private final NoDslContextBeanFailureAnalyzer failureAnalyzer = new NoDslContextBeanFailureAnalyzer();
@Test
void noAnalysisWithoutR2dbcAutoConfiguration() {
new ApplicationContextRunner().run((context) -> {
this.failureAnalyzer.setBeanFactory(context.getBeanFactory());
assertThat(this.failureAnalyzer.analyze(new NoSuchBeanDefinitionException(DSLContext.class))).isNull();
});
}
@Test
void analysisWithR2dbcAutoConfiguration() {
new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(R2dbcAutoConfiguration.class))
.run((context) -> {
this.failureAnalyzer.setBeanFactory(context.getBeanFactory());
assertThat(this.failureAnalyzer.analyze(new NoSuchBeanDefinitionException(DSLContext.class)))
.isNotNull();
});
}
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright 2012-2022 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 java.sql.Connection;
import java.sql.DatabaseMetaData;
import javax.sql.DataSource;
import org.jooq.SQLDialect;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link SqlDialectLookup}.
*
* @author Michael Simons
* @author Stephane Nicoll
*/
class SqlDialectLookupTests {
@Test
void getSqlDialectWhenDataSourceIsNullShouldReturnDefault() {
assertThat(SqlDialectLookup.getDialect(null)).isEqualTo(SQLDialect.DEFAULT);
}
@Test
void getSqlDialectWhenDataSourceIsUnknownShouldReturnDefault() throws Exception {
testGetSqlDialect("jdbc:idontexist:", SQLDialect.DEFAULT);
}
@Test
void getSqlDialectWhenDerbyShouldReturnDerby() throws Exception {
testGetSqlDialect("jdbc:derby:", SQLDialect.DERBY);
}
@Test
void getSqlDialectWhenH2ShouldReturnH2() throws Exception {
testGetSqlDialect("jdbc:h2:", SQLDialect.H2);
}
@Test
void getSqlDialectWhenHsqldbShouldReturnHsqldb() throws Exception {
testGetSqlDialect("jdbc:hsqldb:", SQLDialect.HSQLDB);
}
@Test
void getSqlDialectWhenMysqlShouldReturnMysql() throws Exception {
testGetSqlDialect("jdbc:mysql:", SQLDialect.MYSQL);
}
@Test
void getSqlDialectWhenOracleShouldReturnDefault() throws Exception {
testGetSqlDialect("jdbc:oracle:", SQLDialect.DEFAULT);
}
@Test
void getSqlDialectWhenPostgresShouldReturnPostgres() throws Exception {
testGetSqlDialect("jdbc:postgresql:", SQLDialect.POSTGRES);
}
@Test
void getSqlDialectWhenSqlserverShouldReturnDefault() throws Exception {
testGetSqlDialect("jdbc:sqlserver:", SQLDialect.DEFAULT);
}
@Test
void getSqlDialectWhenDb2ShouldReturnDefault() throws Exception {
testGetSqlDialect("jdbc:db2:", SQLDialect.DEFAULT);
}
@Test
void getSqlDialectWhenInformixShouldReturnDefault() throws Exception {
testGetSqlDialect("jdbc:informix-sqli:", SQLDialect.DEFAULT);
}
private void testGetSqlDialect(String url, SQLDialect expected) throws Exception {
DataSource dataSource = mock(DataSource.class);
Connection connection = mock(Connection.class);
DatabaseMetaData metaData = mock(DatabaseMetaData.class);
given(dataSource.getConnection()).willReturn(connection);
given(connection.getMetaData()).willReturn(metaData);
given(metaData.getURL()).willReturn(url);
SQLDialect sqlDialect = SqlDialectLookup.getDialect(dataSource);
assertThat(sqlDialect).isEqualTo(expected);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2021 the original author or authors.
* Copyright 2012-2022 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.
@@ -40,6 +40,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
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.jdbc.DataSourceBuilder;
import org.springframework.boot.test.context.FilteredClassLoader;
@@ -383,6 +384,25 @@ class LiquibaseAutoConfigurationTests {
.run(assertLiquibase((liquibase) -> assertThat(liquibase.getTag()).isEqualTo("1.0.0")));
}
@Test
void whenLiquibaseIsAutoConfiguredThenJooqDslContextDependsOnSpringLiquibaseBeans() {
this.contextRunner.withConfiguration(AutoConfigurations.of(JooqAutoConfiguration.class))
.withUserConfiguration(EmbeddedDataSourceConfiguration.class).run((context) -> {
BeanDefinition beanDefinition = context.getBeanFactory().getBeanDefinition("dslContext");
assertThat(beanDefinition.getDependsOn()).containsExactly("liquibase");
});
}
@Test
void whenCustomSpringLiquibaseIsDefinedThenJooqDslContextDependsOnSpringLiquibaseBeans() {
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);