Skip to content

Commit 4de9109

Browse files
committed
Polish 'Add conditional bean for jOOQ translator'
See gh-38762
1 parent c3aa953 commit 4de9109

File tree

8 files changed

+330
-119
lines changed

8 files changed

+330
-119
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

+7-6
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,14 +70,15 @@ public SpringTransactionProvider transactionProvider(PlatformTransactionManager
7070

7171
@Bean
7272
@Order(0)
73-
public DefaultExecuteListenerProvider jooqExceptionTranslatorExecuteListenerProvider(JooqExceptionTranslatorListener jooqExceptionTranslator) {
74-
return new DefaultExecuteListenerProvider(jooqExceptionTranslator);
73+
public DefaultExecuteListenerProvider jooqExceptionTranslatorExecuteListenerProvider(
74+
ExceptionTranslatorExecuteListener exceptionTranslatorExecuteListener) {
75+
return new DefaultExecuteListenerProvider(exceptionTranslatorExecuteListener);
7576
}
7677

7778
@Bean
78-
@ConditionalOnMissingBean(JooqExceptionTranslatorListener.class)
79-
public JooqExceptionTranslatorListener jooqExceptionTranslator() {
80-
return new JooqExceptionTranslator();
79+
@ConditionalOnMissingBean(ExceptionTranslatorExecuteListener.class)
80+
public ExceptionTranslatorExecuteListener jooqExceptionTranslator() {
81+
return ExceptionTranslatorExecuteListener.DEFAULT;
8182
}
8283

8384
@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,79 +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;
24-
import org.jooq.SQLDialect;
23+
import org.jooq.ExecuteListener;
2524

2625
import org.springframework.dao.DataAccessException;
27-
import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator;
28-
import org.springframework.jdbc.support.SQLExceptionTranslator;
29-
import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
3026

3127
/**
32-
* Transforms {@link SQLException} into a Spring-specific
33-
* {@link DataAccessException}.
28+
* Transforms {@link SQLException} into a Spring-specific {@link DataAccessException}.
3429
*
3530
* @author Lukas Eder
3631
* @author Andreas Ahlenstorf
3732
* @author Phillip Webb
3833
* @author Stephane Nicoll
3934
* @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}
4038
*/
41-
public class JooqExceptionTranslator implements JooqExceptionTranslatorListener {
39+
@Deprecated(since = "3.3.0", forRemoval = true)
40+
public class JooqExceptionTranslator implements ExecuteListener {
4241

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

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

9650
}

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

-38
This file was deleted.

0 commit comments

Comments
 (0)