Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ abstract class AbstractJooqConfigurationFactory {
* @return A {@link org.jooq.Configuration}
*/
@SuppressWarnings("checkstyle:ParameterNumber")
protected DefaultConfiguration jooqConfiguration(
protected <T extends DefaultConfiguration> T configureConfiguration(
T configuration,
@Parameter String name,
@Parameter @Nullable TransactionProvider transactionProvider,
@Parameter @Nullable Settings settings,
Expand All @@ -72,8 +73,6 @@ protected DefaultConfiguration jooqConfiguration(
AbstractJooqConfigurationProperties properties,
ApplicationContext ctx
) {
DefaultConfiguration configuration = new DefaultConfiguration();

if (transactionProvider != null) {
configuration.setTransactionProvider(transactionProvider);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,19 @@
*/
package io.micronaut.configuration.jooq;

import io.micronaut.context.BeanProvider;
import io.micronaut.context.annotation.Any;
import io.micronaut.context.annotation.EachBean;
import io.micronaut.context.annotation.Factory;
import io.micronaut.context.annotation.Parameter;
import io.micronaut.context.annotation.Primary;
import io.micronaut.context.annotation.Requires;
import io.micronaut.context.annotation.Secondary;
import io.micronaut.core.annotation.Internal;
import io.micronaut.inject.qualifiers.Qualifiers;
import io.micronaut.transaction.TransactionStatus;
import io.micronaut.transaction.support.ExceptionUtil;
import jakarta.inject.Singleton;
import org.jooq.Configuration;
import org.jooq.DSLContext;
import org.jooq.TransactionProperty;
Expand All @@ -43,8 +51,47 @@ final class DSLContextFactory {
* @param configuration The {@link Configuration}
* @return A {@link DSLContext}
*/
@EachBean(Configuration.class)

@radovanradic radovanradic Apr 21, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this cause regression for existing apps where users provide Configuration bean, this might not work then since it will expect Jdbc/R2dbc/Configuration?
This test would fail (passing in 7.0.x)

void "test dsl context is created for custom jooq configuration bean"() {
        given:
        ApplicationContext applicationContext = ApplicationContext.run("custom-configuration")

        expect:
        applicationContext.containsBean(Configuration)
        applicationContext.containsBean(DSLContext)
        applicationContext.getBean(DSLContext).configuration().dialect() == SQLDialect.POSTGRES

        cleanup:
        applicationContext.close()
    }

    @Factory
    @Requires(env = "custom-configuration")
    static class TestFactory {

        @Singleton
        @Named("default")
        Configuration configuration() {
            DefaultConfiguration configuration = new DefaultConfiguration()
            configuration.setSQLDialect(SQLDialect.POSTGRES)
            return configuration
        }
    }

DSLContext dslContext(Configuration configuration) {
@EachBean(JdbcConfiguration.class)
@Secondary
DSLContext dslContext(@Parameter JdbcConfiguration configuration) {
return createDslContext(configuration);
}

@EachBean(R2dbcConfiguration.class)
@Secondary
DSLContext r2dbcDslContext(@Parameter R2dbcConfiguration configuration) {
return createDslContext(configuration);
}

@Primary
@Singleton
@Requires(beans = R2dbcConfiguration.class)
DSLContext primaryR2dbcDslContext(@Any BeanProvider<R2dbcConfiguration> configurations) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could the new primary alias duplicate one DSLContext?

R2dbcConfiguration configuration = selectR2dbcConfiguration(configurations);
return createDslContext(configuration);
}

private R2dbcConfiguration selectR2dbcConfiguration(BeanProvider<R2dbcConfiguration> configurations) {
return configurations.find(Qualifiers.byStereotype(Primary.class))
.or(() -> configurations.find(Qualifiers.byName("default")))
.orElseGet(() -> requireSingleConfiguration(configurations));
}

private R2dbcConfiguration requireSingleConfiguration(BeanProvider<R2dbcConfiguration> configurations) {
var iterator = configurations.iterator();
var configuration = iterator.next();
if (iterator.hasNext()) {
int candidateCount = 2;
while (iterator.hasNext()) {
iterator.next();
candidateCount++;
}
throw new IllegalStateException("Multiple R2DBC configurations found (" + candidateCount + "). Mark one @Primary or name one 'default'");
}
return configuration;
}

private DSLContext createDslContext(Configuration configuration) {
return new DefaultDSLContext(configuration) {

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2017-2026 original 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 io.micronaut.configuration.jooq;

import io.micronaut.core.annotation.Internal;
import org.jooq.impl.DefaultConfiguration;

/**
* Internal JDBC-backed jOOQ configuration bean type.
*
* @since 7.0.0
*/
@Internal
final class JdbcConfiguration extends DefaultConfiguration {
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import io.micronaut.context.annotation.Factory;
import io.micronaut.context.annotation.Parameter;
import io.micronaut.core.annotation.Internal;
import org.jspecify.annotations.Nullable;
import io.micronaut.jdbc.DataSourceResolver;
import org.jooq.Configuration;
import org.jooq.ConnectionProvider;
Expand All @@ -31,7 +30,7 @@
import org.jooq.RecordUnmapperProvider;
import org.jooq.TransactionProvider;
import org.jooq.conf.Settings;
import org.jooq.impl.DefaultConfiguration;
import org.jspecify.annotations.Nullable;

import javax.sql.DataSource;

Expand Down Expand Up @@ -66,7 +65,7 @@ final class JooqConfigurationFactory extends AbstractJooqConfigurationFactory {
*/
@SuppressWarnings("checkstyle:ParameterNumber")
@EachBean(DataSource.class)
Configuration jooqConfiguration(
JdbcConfiguration jooqConfiguration(
@Parameter String name,
DataSource dataSource,
@Parameter @Nullable TransactionProvider transactionProvider,
Expand All @@ -89,7 +88,7 @@ Configuration jooqConfiguration(
dataSourceResolver = DataSourceResolver.DEFAULT;
}

DefaultConfiguration configuration = super.jooqConfiguration(name, transactionProvider, settings, executorProvider,
JdbcConfiguration configuration = super.configureConfiguration(new JdbcConfiguration(), name, transactionProvider, settings, executorProvider,
recordMapperProvider, recordUnmapperProvider, metaProvider, converterProvider, properties, ctx);

if (connectionProvider != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2017-2026 original 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 io.micronaut.configuration.jooq;

import io.micronaut.core.annotation.Internal;
import org.jooq.impl.DefaultConfiguration;

/**
* Internal R2DBC-backed jOOQ configuration bean type.
*
* @since 7.0.0
*/
@Internal
final class R2dbcConfiguration extends DefaultConfiguration {
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import io.micronaut.context.annotation.Parameter;
import io.micronaut.context.annotation.Requires;
import io.micronaut.core.annotation.Internal;
import org.jspecify.annotations.Nullable;
import io.r2dbc.spi.ConnectionFactory;
import org.jooq.Configuration;
import org.jooq.ConverterProvider;
Expand All @@ -32,7 +31,7 @@
import org.jooq.SQLDialect;
import org.jooq.TransactionProvider;
import org.jooq.conf.Settings;
import org.jooq.impl.DefaultConfiguration;
import org.jspecify.annotations.Nullable;

/**
* Sets up R2DBC jOOQ library integration.
Expand Down Expand Up @@ -64,7 +63,7 @@ final class R2dbcJooqConfigurationFactory extends AbstractJooqConfigurationFacto
*/
@SuppressWarnings("checkstyle:MethodLength")
@EachBean(ConnectionFactory.class)
Configuration jooqConfiguration(
R2dbcConfiguration jooqConfiguration(
@Parameter String name,
ConnectionFactory connectionFactory,
@Parameter @Nullable TransactionProvider transactionProvider,
Expand All @@ -81,7 +80,7 @@ Configuration jooqConfiguration(
properties = new R2dbcJooqConfigurationProperties();
}

DefaultConfiguration configuration = super.jooqConfiguration(name, transactionProvider, settings, executorProvider,
R2dbcConfiguration configuration = super.configureConfiguration(new R2dbcConfiguration(), name, transactionProvider, settings, executorProvider,
recordMapperProvider, recordUnmapperProvider, metaProvider, converterProvider, properties, ctx);

configuration.setSQLDialect(getSqlDialect(properties));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Copyright 2017-2022 original 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 io.micronaut.configuration.jooq

import io.micronaut.context.ApplicationContext
import io.micronaut.context.annotation.Factory
import io.micronaut.context.annotation.Requires
import io.r2dbc.spi.ConnectionFactory
import jakarta.inject.Named
import jakarta.inject.Singleton
import org.jooq.DSLContext
import org.jooq.SQLDialect
import spock.lang.Specification

import javax.sql.DataSource
import java.lang.reflect.Proxy

class DslContextFactorySpec extends Specification {

void "test default dsl context can be injected with jdbc and r2dbc configurations"() {
given:
ApplicationContext applicationContext = ApplicationContext.run("multiple-config")

when:
TestService testService = applicationContext.getBean(TestService)

then:
testService.dslContext != null
testService.dslContext.configuration() instanceof R2dbcConfiguration

cleanup:
applicationContext.close()
}

@Singleton
@Requires(env = "multiple-config")
static class TestService {
final DSLContext dslContext

TestService(DSLContext dslContext) {
this.dslContext = dslContext
}
}

@Factory
@Requires(env = "multiple-config")
static class TestConnectionFactory {

@Singleton
@Named("default")
DataSource dataSource() {
return proxy(DataSource)
}

@Singleton
@Named("default")
ConnectionFactory connectionFactory() {
return proxy(ConnectionFactory)
}

@Singleton
@Named("analytics")
ConnectionFactory analyticsConnectionFactory() {
return proxy(ConnectionFactory)
}

@Singleton
@Named("default")
JooqConfigurationProperties jdbcJooqProperties() {
JooqConfigurationProperties configurationProperties = new JooqConfigurationProperties()
configurationProperties.sqlDialect = SQLDialect.H2
return configurationProperties
}

@Singleton
@Named("default")
R2dbcJooqConfigurationProperties r2dbcJooqProperties() {
R2dbcJooqConfigurationProperties configurationProperties = new R2dbcJooqConfigurationProperties()
configurationProperties.sqlDialect = SQLDialect.POSTGRES
return configurationProperties
}

@Singleton
@Named("analytics")
R2dbcJooqConfigurationProperties analyticsR2dbcJooqProperties() {
R2dbcJooqConfigurationProperties configurationProperties = new R2dbcJooqConfigurationProperties()
configurationProperties.sqlDialect = SQLDialect.MYSQL
return configurationProperties
}

private static <T> T proxy(Class<T> type) {
return (T) Proxy.newProxyInstance(type.classLoader, [type] as Class<?>[]) { _, method, _ ->
if (method.name == "toString") {
return type.simpleName
}
if (method.returnType == Boolean.TYPE) {
return false
}
if (method.returnType == Integer.TYPE) {
return 0
}
return null
}
}
}
}
Loading