Reinstate support for jOOQ as it now supports Jakarta EE 9
Closes gh-29271
This commit is contained in:
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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,\
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user