diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/support/AbstractFallbackSQLExceptionTranslator.java b/spring-jdbc/src/main/java/org/springframework/jdbc/support/AbstractFallbackSQLExceptionTranslator.java index 44b8838b210..f82e8475ce1 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/support/AbstractFallbackSQLExceptionTranslator.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/support/AbstractFallbackSQLExceptionTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2023 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. @@ -26,11 +26,15 @@ import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** - * Base class for {@link SQLExceptionTranslator} implementations that allow for - * fallback to some other {@link SQLExceptionTranslator}. + * Base class for {@link SQLExceptionTranslator} implementations that allow for a + * fallback to some other {@link SQLExceptionTranslator}, as well as for custom + * overrides. * * @author Juergen Hoeller * @since 2.5.6 + * @see #doTranslate + * @see #setFallbackTranslator + * @see #setCustomTranslator */ public abstract class AbstractFallbackSQLExceptionTranslator implements SQLExceptionTranslator { @@ -40,10 +44,13 @@ public abstract class AbstractFallbackSQLExceptionTranslator implements SQLExcep @Nullable private SQLExceptionTranslator fallbackTranslator; + @Nullable + private SQLExceptionTranslator customTranslator; + /** - * Override the default SQL state fallback translator - * (typically a {@link SQLStateSQLExceptionTranslator}). + * Set the fallback translator to use when this translator cannot find a + * specific match itself. */ public void setFallbackTranslator(@Nullable SQLExceptionTranslator fallback) { this.fallbackTranslator = fallback; @@ -51,12 +58,33 @@ public abstract class AbstractFallbackSQLExceptionTranslator implements SQLExcep /** * Return the fallback exception translator, if any. + * @see #setFallbackTranslator */ @Nullable public SQLExceptionTranslator getFallbackTranslator() { return this.fallbackTranslator; } + /** + * Set a custom exception translator to override any match that this translator + * would find. Note that such a custom {@link SQLExceptionTranslator} delegate + * is meant to return {@code null} if it does not have an override itself. + * @since 6.1 + */ + public void setCustomTranslator(@Nullable SQLExceptionTranslator customTranslator) { + this.customTranslator = customTranslator; + } + + /** + * Return a custom exception translator, if any. + * @since 6.1 + * @see #setCustomTranslator + */ + @Nullable + public SQLExceptionTranslator getCustomTranslator() { + return this.customTranslator; + } + /** * Pre-checks the arguments, calls {@link #doTranslate}, and invokes the @@ -67,6 +95,15 @@ public abstract class AbstractFallbackSQLExceptionTranslator implements SQLExcep public DataAccessException translate(String task, @Nullable String sql, SQLException ex) { Assert.notNull(ex, "Cannot translate a null SQLException"); + SQLExceptionTranslator custom = getCustomTranslator(); + if (custom != null) { + DataAccessException dae = custom.translate(task, sql, ex); + if (dae != null) { + // Custom exception match found. + return dae; + } + } + DataAccessException dae = doTranslate(task, sql, ex); if (dae != null) { // Specific exception match found. diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodeSQLExceptionTranslator.java b/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodeSQLExceptionTranslator.java index 25d1513a782..16c8c092fbc 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodeSQLExceptionTranslator.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodeSQLExceptionTranslator.java @@ -60,6 +60,11 @@ import org.springframework.util.function.SupplierUtils; * of the class path (e.g. in the "/WEB-INF/classes" directory), as long as the * Spring JDBC package is loaded from the same ClassLoader. * + *

This translator is commonly used by default if a user-provided `sql-error-codes.xml` + * file has been found in the root of the classpath, as a signal to use this strategy. + * Otherwise, {@link SQLExceptionSubclassTranslator} serves as the default translator + * as of 6.0. + * * @author Rod Johnson * @author Thomas Risberg * @author Juergen Hoeller @@ -75,11 +80,10 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep private static final int MESSAGE_SQL_THROWABLE_CONSTRUCTOR = 4; private static final int MESSAGE_SQL_SQLEX_CONSTRUCTOR = 5; - private static final boolean USER_PROVIDED_ERROR_CODES_FILE_PRESENT = - new ClassPathResource(SQLErrorCodesFactory.SQL_ERROR_CODE_OVERRIDE_PATH, SQLErrorCodesFactory.class.getClassLoader()).exists(); - + private static final boolean userProvidedErrorCodesFilePresent = + new ClassPathResource(SQLErrorCodesFactory.SQL_ERROR_CODE_OVERRIDE_PATH, + SQLErrorCodesFactory.class.getClassLoader()).exists(); - /** Error codes used by this translator. */ @Nullable private SingletonSupplier sqlErrorCodes; @@ -198,9 +202,9 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep if (sqlErrorCodes != null) { SQLExceptionTranslator customTranslator = sqlErrorCodes.getCustomSqlExceptionTranslator(); if (customTranslator != null) { - DataAccessException customDex = customTranslator.translate(task, sql, sqlEx); - if (customDex != null) { - return customDex; + dae = customTranslator.translate(task, sql, sqlEx); + if (dae != null) { + return dae; } } } @@ -228,11 +232,10 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep for (CustomSQLErrorCodesTranslation customTranslation : customTranslations) { if (Arrays.binarySearch(customTranslation.getErrorCodes(), errorCode) >= 0 && customTranslation.getExceptionClass() != null) { - DataAccessException customException = createCustomException( - task, sql, sqlEx, customTranslation.getExceptionClass()); - if (customException != null) { + dae = createCustomException(task, sql, sqlEx, customTranslation.getExceptionClass()); + if (dae != null) { logTranslation(task, sql, sqlEx, true); - return customException; + return dae; } } } @@ -306,7 +309,9 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep * resulting from custom translation. This exception should include the {@code sqlEx} parameter * as a nested root cause. This implementation always returns {@code null}, meaning that the * translator always falls back to the default error codes. + * @deprecated as of 6.1, in favor of {@link #setCustomTranslator} */ + @Deprecated(since = "6.1") @Nullable protected DataAccessException customTranslate(String task, @Nullable String sql, SQLException sqlEx) { return null; @@ -426,7 +431,7 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep * in the root of the classpath. */ static boolean hasUserProvidedErrorCodesFile() { - return USER_PROVIDED_ERROR_CODES_FILE_PRESENT; + return userProvidedErrorCodesFilePresent; } }