Skip to content

Commit cf8e06a

Browse files
committed
Merge pull request #38762 from MelleD
* pr/38762: Polish 'Add conditional bean for jOOQ translator' Add conditional bean for jOOQ translator Closes gh-38762
2 parents 83fd4fb + 4de9109 commit cf8e06a

File tree

7 files changed

+355
-61
lines changed

7 files changed

+355
-61
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.jooq;
18+
19+
import java.sql.SQLException;
20+
import java.util.function.Function;
21+
22+
import org.apache.commons.logging.Log;
23+
import org.apache.commons.logging.LogFactory;
24+
import org.jooq.ExecuteContext;
25+
import org.jooq.SQLDialect;
26+
27+
import org.springframework.dao.DataAccessException;
28+
import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator;
29+
import org.springframework.jdbc.support.SQLExceptionTranslator;
30+
import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
31+
import org.springframework.util.Assert;
32+
33+
/**
34+
* Default implementation of {@link ExceptionTranslatorExecuteListener} that delegates to
35+
* an {@link SQLExceptionTranslator}.
36+
*
37+
* @author Lukas Eder
38+
* @author Andreas Ahlenstorf
39+
* @author Phillip Webb
40+
* @author Stephane Nicoll
41+
*/
42+
final class DefaultExceptionTranslatorExecuteListener implements ExceptionTranslatorExecuteListener {
43+
44+
// Based on the jOOQ-spring-example from https://github.com/jOOQ/jOOQ
45+
46+
private static final Log defaultLogger = LogFactory.getLog(ExceptionTranslatorExecuteListener.class);
47+
48+
private final Log logger;
49+
50+
private Function<ExecuteContext, SQLExceptionTranslator> translatorFactory;
51+
52+
DefaultExceptionTranslatorExecuteListener() {
53+
this(defaultLogger, new DefaultTranslatorFactory());
54+
}
55+
56+
DefaultExceptionTranslatorExecuteListener(Function<ExecuteContext, SQLExceptionTranslator> translatorFactory) {
57+
this(defaultLogger, translatorFactory);
58+
}
59+
60+
DefaultExceptionTranslatorExecuteListener(Log logger) {
61+
this(logger, new DefaultTranslatorFactory());
62+
}
63+
64+
private DefaultExceptionTranslatorExecuteListener(Log logger,
65+
Function<ExecuteContext, SQLExceptionTranslator> translatorFactory) {
66+
Assert.notNull(translatorFactory, "TranslatorFactory must not be null");
67+
this.logger = logger;
68+
this.translatorFactory = translatorFactory;
69+
}
70+
71+
@Override
72+
public void exception(ExecuteContext context) {
73+
SQLExceptionTranslator translator = this.translatorFactory.apply(context);
74+
// The exception() callback is not only triggered for SQL exceptions but also for
75+
// "normal" exceptions. In those cases sqlException() returns null.
76+
SQLException exception = context.sqlException();
77+
while (exception != null) {
78+
handle(context, translator, exception);
79+
exception = exception.getNextException();
80+
}
81+
}
82+
83+
/**
84+
* Handle a single exception in the chain. SQLExceptions might be nested multiple
85+
* levels deep. The outermost exception is usually the least interesting one ("Call
86+
* getNextException to see the cause."). Therefore the innermost exception is
87+
* propagated and all other exceptions are logged.
88+
* @param context the execute context
89+
* @param translator the exception translator
90+
* @param exception the exception
91+
*/
92+
private void handle(ExecuteContext context, SQLExceptionTranslator translator, SQLException exception) {
93+
DataAccessException translated = translator.translate("jOOQ", context.sql(), exception);
94+
if (exception.getNextException() != null) {
95+
this.logger.error("Execution of SQL statement failed.", (translated != null) ? translated : exception);
96+
return;
97+
}
98+
if (translated != null) {
99+
context.exception(translated);
100+
}
101+
}
102+
103+
/**
104+
* Default {@link SQLExceptionTranslator} factory that creates the translator based on
105+
* the Spring DB name.
106+
*/
107+
private static final class DefaultTranslatorFactory implements Function<ExecuteContext, SQLExceptionTranslator> {
108+
109+
@Override
110+
public SQLExceptionTranslator apply(ExecuteContext context) {
111+
return apply(context.configuration().dialect());
112+
}
113+
114+
private SQLExceptionTranslator apply(SQLDialect dialect) {
115+
String dbName = getSpringDbName(dialect);
116+
return (dbName != null) ? new SQLErrorCodeSQLExceptionTranslator(dbName)
117+
: new SQLStateSQLExceptionTranslator();
118+
}
119+
120+
private String getSpringDbName(SQLDialect dialect) {
121+
return (dialect != null && dialect.thirdParty() != null) ? dialect.thirdParty().springDbName() : null;
122+
}
123+
124+
}
125+
126+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.jooq;
18+
19+
import java.sql.SQLException;
20+
import java.util.function.Function;
21+
22+
import org.jooq.ExecuteContext;
23+
import org.jooq.ExecuteListener;
24+
import org.jooq.impl.DefaultExecuteListenerProvider;
25+
26+
import org.springframework.dao.DataAccessException;
27+
import org.springframework.jdbc.support.SQLExceptionTranslator;
28+
29+
/**
30+
* An {@link ExecuteListener} used by the auto-configured
31+
* {@link DefaultExecuteListenerProvider} to translate exceptions in the
32+
* {@link ExecuteContext}. Most commonly used to translate {@link SQLException
33+
* SQLExceptions} to Spring-specific {@link DataAccessException DataAccessExceptions} by
34+
* adapting an existing {@link SQLExceptionTranslator}.
35+
*
36+
* @author Dennis Melzer
37+
* @since 3.3.0
38+
* @see #DEFAULT
39+
* @see #of(Function)
40+
*/
41+
public interface ExceptionTranslatorExecuteListener extends ExecuteListener {
42+
43+
/**
44+
* Default {@link ExceptionTranslatorExecuteListener} suitable for most applications.
45+
*/
46+
ExceptionTranslatorExecuteListener DEFAULT = new DefaultExceptionTranslatorExecuteListener();
47+
48+
/**
49+
* Creates a new {@link ExceptionTranslatorExecuteListener} backed by an
50+
* {@link SQLExceptionTranslator}.
51+
* @param translatorFactory factory function used to create the
52+
* {@link SQLExceptionTranslator}
53+
* @return a new {@link ExceptionTranslatorExecuteListener} instance
54+
*/
55+
static ExceptionTranslatorExecuteListener of(Function<ExecuteContext, SQLExceptionTranslator> translatorFactory) {
56+
return new DefaultExceptionTranslatorExecuteListener(translatorFactory);
57+
}
58+
59+
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/JooqAutoConfiguration.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -70,8 +70,15 @@ public SpringTransactionProvider transactionProvider(PlatformTransactionManager
7070

7171
@Bean
7272
@Order(0)
73-
public DefaultExecuteListenerProvider jooqExceptionTranslatorExecuteListenerProvider() {
74-
return new DefaultExecuteListenerProvider(new JooqExceptionTranslator());
73+
public DefaultExecuteListenerProvider jooqExceptionTranslatorExecuteListenerProvider(
74+
ExceptionTranslatorExecuteListener exceptionTranslatorExecuteListener) {
75+
return new DefaultExecuteListenerProvider(exceptionTranslatorExecuteListener);
76+
}
77+
78+
@Bean
79+
@ConditionalOnMissingBean(ExceptionTranslatorExecuteListener.class)
80+
public ExceptionTranslatorExecuteListener jooqExceptionTranslator() {
81+
return ExceptionTranslatorExecuteListener.DEFAULT;
7582
}
7683

7784
@Configuration(proxyBeanMethods = false)
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2022 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,80 +18,33 @@
1818

1919
import java.sql.SQLException;
2020

21-
import org.apache.commons.logging.Log;
2221
import org.apache.commons.logging.LogFactory;
2322
import org.jooq.ExecuteContext;
2423
import org.jooq.ExecuteListener;
25-
import org.jooq.SQLDialect;
2624

2725
import org.springframework.dao.DataAccessException;
28-
import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator;
29-
import org.springframework.jdbc.support.SQLExceptionTranslator;
30-
import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
3126

3227
/**
33-
* Transforms {@link java.sql.SQLException} into a Spring-specific
34-
* {@link DataAccessException}.
28+
* Transforms {@link SQLException} into a Spring-specific {@link DataAccessException}.
3529
*
3630
* @author Lukas Eder
3731
* @author Andreas Ahlenstorf
3832
* @author Phillip Webb
3933
* @author Stephane Nicoll
4034
* @since 1.5.10
35+
* @deprecated since 3.3.0 for removal in 3.5.0 in favor of
36+
* {@link ExceptionTranslatorExecuteListener#DEFAULT} or
37+
* {@link ExceptionTranslatorExecuteListener#of}
4138
*/
39+
@Deprecated(since = "3.3.0", forRemoval = true)
4240
public class JooqExceptionTranslator implements ExecuteListener {
4341

44-
// Based on the jOOQ-spring-example from https://github.com/jOOQ/jOOQ
45-
46-
private static final Log logger = LogFactory.getLog(JooqExceptionTranslator.class);
42+
private final DefaultExceptionTranslatorExecuteListener delegate = new DefaultExceptionTranslatorExecuteListener(
43+
LogFactory.getLog(JooqExceptionTranslator.class));
4744

4845
@Override
4946
public void exception(ExecuteContext context) {
50-
SQLExceptionTranslator translator = getTranslator(context);
51-
// The exception() callback is not only triggered for SQL exceptions but also for
52-
// "normal" exceptions. In those cases sqlException() returns null.
53-
SQLException exception = context.sqlException();
54-
while (exception != null) {
55-
handle(context, translator, exception);
56-
exception = exception.getNextException();
57-
}
58-
}
59-
60-
private SQLExceptionTranslator getTranslator(ExecuteContext context) {
61-
SQLDialect dialect = context.configuration().dialect();
62-
if (dialect != null && dialect.thirdParty() != null) {
63-
String dbName = dialect.thirdParty().springDbName();
64-
if (dbName != null) {
65-
return new SQLErrorCodeSQLExceptionTranslator(dbName);
66-
}
67-
}
68-
return new SQLStateSQLExceptionTranslator();
69-
}
70-
71-
/**
72-
* Handle a single exception in the chain. SQLExceptions might be nested multiple
73-
* levels deep. The outermost exception is usually the least interesting one ("Call
74-
* getNextException to see the cause."). Therefore the innermost exception is
75-
* propagated and all other exceptions are logged.
76-
* @param context the execute context
77-
* @param translator the exception translator
78-
* @param exception the exception
79-
*/
80-
private void handle(ExecuteContext context, SQLExceptionTranslator translator, SQLException exception) {
81-
DataAccessException translated = translate(context, translator, exception);
82-
if (exception.getNextException() == null) {
83-
if (translated != null) {
84-
context.exception(translated);
85-
}
86-
}
87-
else {
88-
logger.error("Execution of SQL statement failed.", (translated != null) ? translated : exception);
89-
}
90-
}
91-
92-
private DataAccessException translate(ExecuteContext context, SQLExceptionTranslator translator,
93-
SQLException exception) {
94-
return translator.translate("jOOQ", context.sql(), exception);
47+
this.delegate.exception(context);
9548
}
9649

9750
}

0 commit comments

Comments
 (0)