Add support for configurable custom translator
Closes gh-24634
This commit is contained in:
parent
52c19272c6
commit
519927421e
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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…
Reference in New Issue