Browse Source

Add support for configurable custom translator

Closes gh-24634
pull/30891/head
Juergen Hoeller 2 years ago
parent
commit
519927421e
  1. 47
      spring-jdbc/src/main/java/org/springframework/jdbc/support/AbstractFallbackSQLExceptionTranslator.java
  2. 29
      spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodeSQLExceptionTranslator.java

47
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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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; import org.springframework.util.Assert;
/** /**
* Base class for {@link SQLExceptionTranslator} implementations that allow for * Base class for {@link SQLExceptionTranslator} implementations that allow for a
* fallback to some other {@link SQLExceptionTranslator}. * fallback to some other {@link SQLExceptionTranslator}, as well as for custom
* overrides.
* *
* @author Juergen Hoeller * @author Juergen Hoeller
* @since 2.5.6 * @since 2.5.6
* @see #doTranslate
* @see #setFallbackTranslator
* @see #setCustomTranslator
*/ */
public abstract class AbstractFallbackSQLExceptionTranslator implements SQLExceptionTranslator { public abstract class AbstractFallbackSQLExceptionTranslator implements SQLExceptionTranslator {
@ -40,10 +44,13 @@ public abstract class AbstractFallbackSQLExceptionTranslator implements SQLExcep
@Nullable @Nullable
private SQLExceptionTranslator fallbackTranslator; private SQLExceptionTranslator fallbackTranslator;
@Nullable
private SQLExceptionTranslator customTranslator;
/** /**
* Override the default SQL state fallback translator * Set the fallback translator to use when this translator cannot find a
* (typically a {@link SQLStateSQLExceptionTranslator}). * specific match itself.
*/ */
public void setFallbackTranslator(@Nullable SQLExceptionTranslator fallback) { public void setFallbackTranslator(@Nullable SQLExceptionTranslator fallback) {
this.fallbackTranslator = fallback; this.fallbackTranslator = fallback;
@ -51,12 +58,33 @@ public abstract class AbstractFallbackSQLExceptionTranslator implements SQLExcep
/** /**
* Return the fallback exception translator, if any. * Return the fallback exception translator, if any.
* @see #setFallbackTranslator
*/ */
@Nullable @Nullable
public SQLExceptionTranslator getFallbackTranslator() { public SQLExceptionTranslator getFallbackTranslator() {
return this.fallbackTranslator; 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 * 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) { public DataAccessException translate(String task, @Nullable String sql, SQLException ex) {
Assert.notNull(ex, "Cannot translate a null SQLException"); 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); DataAccessException dae = doTranslate(task, sql, ex);
if (dae != null) { if (dae != null) {
// Specific exception match found. // Specific exception match found.

29
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 * 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. * Spring JDBC package is loaded from the same ClassLoader.
* *
* <p>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 Rod Johnson
* @author Thomas Risberg * @author Thomas Risberg
* @author Juergen Hoeller * @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_THROWABLE_CONSTRUCTOR = 4;
private static final int MESSAGE_SQL_SQLEX_CONSTRUCTOR = 5; private static final int MESSAGE_SQL_SQLEX_CONSTRUCTOR = 5;
private static final boolean USER_PROVIDED_ERROR_CODES_FILE_PRESENT = private static final boolean userProvidedErrorCodesFilePresent =
new ClassPathResource(SQLErrorCodesFactory.SQL_ERROR_CODE_OVERRIDE_PATH, SQLErrorCodesFactory.class.getClassLoader()).exists(); new ClassPathResource(SQLErrorCodesFactory.SQL_ERROR_CODE_OVERRIDE_PATH,
SQLErrorCodesFactory.class.getClassLoader()).exists();
/** Error codes used by this translator. */
@Nullable @Nullable
private SingletonSupplier<SQLErrorCodes> sqlErrorCodes; private SingletonSupplier<SQLErrorCodes> sqlErrorCodes;
@ -198,9 +202,9 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep
if (sqlErrorCodes != null) { if (sqlErrorCodes != null) {
SQLExceptionTranslator customTranslator = sqlErrorCodes.getCustomSqlExceptionTranslator(); SQLExceptionTranslator customTranslator = sqlErrorCodes.getCustomSqlExceptionTranslator();
if (customTranslator != null) { if (customTranslator != null) {
DataAccessException customDex = customTranslator.translate(task, sql, sqlEx); dae = customTranslator.translate(task, sql, sqlEx);
if (customDex != null) { if (dae != null) {
return customDex; return dae;
} }
} }
} }
@ -228,11 +232,10 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep
for (CustomSQLErrorCodesTranslation customTranslation : customTranslations) { for (CustomSQLErrorCodesTranslation customTranslation : customTranslations) {
if (Arrays.binarySearch(customTranslation.getErrorCodes(), errorCode) >= 0 && if (Arrays.binarySearch(customTranslation.getErrorCodes(), errorCode) >= 0 &&
customTranslation.getExceptionClass() != null) { customTranslation.getExceptionClass() != null) {
DataAccessException customException = createCustomException( dae = createCustomException(task, sql, sqlEx, customTranslation.getExceptionClass());
task, sql, sqlEx, customTranslation.getExceptionClass()); if (dae != null) {
if (customException != null) {
logTranslation(task, sql, sqlEx, true); 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 * 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 * as a nested root cause. This implementation always returns {@code null}, meaning that the
* translator always falls back to the default error codes. * translator always falls back to the default error codes.
* @deprecated as of 6.1, in favor of {@link #setCustomTranslator}
*/ */
@Deprecated(since = "6.1")
@Nullable @Nullable
protected DataAccessException customTranslate(String task, @Nullable String sql, SQLException sqlEx) { protected DataAccessException customTranslate(String task, @Nullable String sql, SQLException sqlEx) {
return null; return null;
@ -426,7 +431,7 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep
* in the root of the classpath. * in the root of the classpath.
*/ */
static boolean hasUserProvidedErrorCodesFile() { static boolean hasUserProvidedErrorCodesFile() {
return USER_PROVIDED_ERROR_CODES_FILE_PRESENT; return userProvidedErrorCodesFilePresent;
} }
} }

Loading…
Cancel
Save