|
|
|
@ -1,5 +1,5 @@ |
|
|
|
/* |
|
|
|
/* |
|
|
|
* Copyright 2002-2018 the original author or authors. |
|
|
|
* Copyright 2002-2020 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. |
|
|
|
@ -35,6 +35,8 @@ import org.springframework.dao.TransientDataAccessResourceException; |
|
|
|
import org.springframework.jdbc.BadSqlGrammarException; |
|
|
|
import org.springframework.jdbc.BadSqlGrammarException; |
|
|
|
import org.springframework.jdbc.InvalidResultSetAccessException; |
|
|
|
import org.springframework.jdbc.InvalidResultSetAccessException; |
|
|
|
import org.springframework.lang.Nullable; |
|
|
|
import org.springframework.lang.Nullable; |
|
|
|
|
|
|
|
import org.springframework.util.function.SingletonSupplier; |
|
|
|
|
|
|
|
import org.springframework.util.function.SupplierUtils; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Implementation of {@link SQLExceptionTranslator} that analyzes vendor-specific error codes. |
|
|
|
* Implementation of {@link SQLExceptionTranslator} that analyzes vendor-specific error codes. |
|
|
|
@ -76,7 +78,7 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep |
|
|
|
|
|
|
|
|
|
|
|
/** Error codes used by this translator. */ |
|
|
|
/** Error codes used by this translator. */ |
|
|
|
@Nullable |
|
|
|
@Nullable |
|
|
|
private SQLErrorCodes sqlErrorCodes; |
|
|
|
private SingletonSupplier<SQLErrorCodes> sqlErrorCodes; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
@ -120,7 +122,7 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public SQLErrorCodeSQLExceptionTranslator(SQLErrorCodes sec) { |
|
|
|
public SQLErrorCodeSQLExceptionTranslator(SQLErrorCodes sec) { |
|
|
|
this(); |
|
|
|
this(); |
|
|
|
this.sqlErrorCodes = sec; |
|
|
|
this.sqlErrorCodes = SingletonSupplier.of(sec); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -134,7 +136,9 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep |
|
|
|
* @see java.sql.DatabaseMetaData#getDatabaseProductName() |
|
|
|
* @see java.sql.DatabaseMetaData#getDatabaseProductName() |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public void setDataSource(DataSource dataSource) { |
|
|
|
public void setDataSource(DataSource dataSource) { |
|
|
|
this.sqlErrorCodes = SQLErrorCodesFactory.getInstance().getErrorCodes(dataSource); |
|
|
|
this.sqlErrorCodes = |
|
|
|
|
|
|
|
SingletonSupplier.of(() -> SQLErrorCodesFactory.getInstance().resolveErrorCodes(dataSource)); |
|
|
|
|
|
|
|
this.sqlErrorCodes.get(); // try early initialization - otherwise the supplier will retry later
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
@ -146,7 +150,7 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep |
|
|
|
* @see java.sql.DatabaseMetaData#getDatabaseProductName() |
|
|
|
* @see java.sql.DatabaseMetaData#getDatabaseProductName() |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public void setDatabaseProductName(String dbName) { |
|
|
|
public void setDatabaseProductName(String dbName) { |
|
|
|
this.sqlErrorCodes = SQLErrorCodesFactory.getInstance().getErrorCodes(dbName); |
|
|
|
this.sqlErrorCodes = SingletonSupplier.of(SQLErrorCodesFactory.getInstance().getErrorCodes(dbName)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
@ -154,7 +158,7 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep |
|
|
|
* @param sec custom error codes to use |
|
|
|
* @param sec custom error codes to use |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public void setSqlErrorCodes(@Nullable SQLErrorCodes sec) { |
|
|
|
public void setSqlErrorCodes(@Nullable SQLErrorCodes sec) { |
|
|
|
this.sqlErrorCodes = sec; |
|
|
|
this.sqlErrorCodes = SingletonSupplier.ofNullable(sec); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
@ -164,7 +168,7 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@Nullable |
|
|
|
@Nullable |
|
|
|
public SQLErrorCodes getSqlErrorCodes() { |
|
|
|
public SQLErrorCodes getSqlErrorCodes() { |
|
|
|
return this.sqlErrorCodes; |
|
|
|
return SupplierUtils.resolve(this.sqlErrorCodes); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -175,7 +179,6 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep |
|
|
|
if (sqlEx instanceof BatchUpdateException && sqlEx.getNextException() != null) { |
|
|
|
if (sqlEx instanceof BatchUpdateException && sqlEx.getNextException() != null) { |
|
|
|
SQLException nestedSqlEx = sqlEx.getNextException(); |
|
|
|
SQLException nestedSqlEx = sqlEx.getNextException(); |
|
|
|
if (nestedSqlEx.getErrorCode() > 0 || nestedSqlEx.getSQLState() != null) { |
|
|
|
if (nestedSqlEx.getErrorCode() > 0 || nestedSqlEx.getSQLState() != null) { |
|
|
|
logger.debug("Using nested SQLException from the BatchUpdateException"); |
|
|
|
|
|
|
|
sqlEx = nestedSqlEx; |
|
|
|
sqlEx = nestedSqlEx; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -187,8 +190,9 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Next, try the custom SQLException translator, if available.
|
|
|
|
// Next, try the custom SQLException translator, if available.
|
|
|
|
if (this.sqlErrorCodes != null) { |
|
|
|
SQLErrorCodes sqlErrorCodes = getSqlErrorCodes(); |
|
|
|
SQLExceptionTranslator customTranslator = this.sqlErrorCodes.getCustomSqlExceptionTranslator(); |
|
|
|
if (sqlErrorCodes != null) { |
|
|
|
|
|
|
|
SQLExceptionTranslator customTranslator = sqlErrorCodes.getCustomSqlExceptionTranslator(); |
|
|
|
if (customTranslator != null) { |
|
|
|
if (customTranslator != null) { |
|
|
|
DataAccessException customDex = customTranslator.translate(task, sql, sqlEx); |
|
|
|
DataAccessException customDex = customTranslator.translate(task, sql, sqlEx); |
|
|
|
if (customDex != null) { |
|
|
|
if (customDex != null) { |
|
|
|
@ -198,9 +202,9 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Check SQLErrorCodes with corresponding error code, if available.
|
|
|
|
// Check SQLErrorCodes with corresponding error code, if available.
|
|
|
|
if (this.sqlErrorCodes != null) { |
|
|
|
if (sqlErrorCodes != null) { |
|
|
|
String errorCode; |
|
|
|
String errorCode; |
|
|
|
if (this.sqlErrorCodes.isUseSqlStateForTranslation()) { |
|
|
|
if (sqlErrorCodes.isUseSqlStateForTranslation()) { |
|
|
|
errorCode = sqlEx.getSQLState(); |
|
|
|
errorCode = sqlEx.getSQLState(); |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else { |
|
|
|
@ -215,7 +219,7 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep |
|
|
|
|
|
|
|
|
|
|
|
if (errorCode != null) { |
|
|
|
if (errorCode != null) { |
|
|
|
// Look for defined custom translations first.
|
|
|
|
// Look for defined custom translations first.
|
|
|
|
CustomSQLErrorCodesTranslation[] customTranslations = this.sqlErrorCodes.getCustomTranslations(); |
|
|
|
CustomSQLErrorCodesTranslation[] customTranslations = sqlErrorCodes.getCustomTranslations(); |
|
|
|
if (customTranslations != null) { |
|
|
|
if (customTranslations != null) { |
|
|
|
for (CustomSQLErrorCodesTranslation customTranslation : customTranslations) { |
|
|
|
for (CustomSQLErrorCodesTranslation customTranslation : customTranslations) { |
|
|
|
if (Arrays.binarySearch(customTranslation.getErrorCodes(), errorCode) >= 0 && |
|
|
|
if (Arrays.binarySearch(customTranslation.getErrorCodes(), errorCode) >= 0 && |
|
|
|
@ -230,43 +234,43 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
// Next, look for grouped error codes.
|
|
|
|
// Next, look for grouped error codes.
|
|
|
|
if (Arrays.binarySearch(this.sqlErrorCodes.getBadSqlGrammarCodes(), errorCode) >= 0) { |
|
|
|
if (Arrays.binarySearch(sqlErrorCodes.getBadSqlGrammarCodes(), errorCode) >= 0) { |
|
|
|
logTranslation(task, sql, sqlEx, false); |
|
|
|
logTranslation(task, sql, sqlEx, false); |
|
|
|
return new BadSqlGrammarException(task, (sql != null ? sql : ""), sqlEx); |
|
|
|
return new BadSqlGrammarException(task, (sql != null ? sql : ""), sqlEx); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (Arrays.binarySearch(this.sqlErrorCodes.getInvalidResultSetAccessCodes(), errorCode) >= 0) { |
|
|
|
else if (Arrays.binarySearch(sqlErrorCodes.getInvalidResultSetAccessCodes(), errorCode) >= 0) { |
|
|
|
logTranslation(task, sql, sqlEx, false); |
|
|
|
logTranslation(task, sql, sqlEx, false); |
|
|
|
return new InvalidResultSetAccessException(task, (sql != null ? sql : ""), sqlEx); |
|
|
|
return new InvalidResultSetAccessException(task, (sql != null ? sql : ""), sqlEx); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (Arrays.binarySearch(this.sqlErrorCodes.getDuplicateKeyCodes(), errorCode) >= 0) { |
|
|
|
else if (Arrays.binarySearch(sqlErrorCodes.getDuplicateKeyCodes(), errorCode) >= 0) { |
|
|
|
logTranslation(task, sql, sqlEx, false); |
|
|
|
logTranslation(task, sql, sqlEx, false); |
|
|
|
return new DuplicateKeyException(buildMessage(task, sql, sqlEx), sqlEx); |
|
|
|
return new DuplicateKeyException(buildMessage(task, sql, sqlEx), sqlEx); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (Arrays.binarySearch(this.sqlErrorCodes.getDataIntegrityViolationCodes(), errorCode) >= 0) { |
|
|
|
else if (Arrays.binarySearch(sqlErrorCodes.getDataIntegrityViolationCodes(), errorCode) >= 0) { |
|
|
|
logTranslation(task, sql, sqlEx, false); |
|
|
|
logTranslation(task, sql, sqlEx, false); |
|
|
|
return new DataIntegrityViolationException(buildMessage(task, sql, sqlEx), sqlEx); |
|
|
|
return new DataIntegrityViolationException(buildMessage(task, sql, sqlEx), sqlEx); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (Arrays.binarySearch(this.sqlErrorCodes.getPermissionDeniedCodes(), errorCode) >= 0) { |
|
|
|
else if (Arrays.binarySearch(sqlErrorCodes.getPermissionDeniedCodes(), errorCode) >= 0) { |
|
|
|
logTranslation(task, sql, sqlEx, false); |
|
|
|
logTranslation(task, sql, sqlEx, false); |
|
|
|
return new PermissionDeniedDataAccessException(buildMessage(task, sql, sqlEx), sqlEx); |
|
|
|
return new PermissionDeniedDataAccessException(buildMessage(task, sql, sqlEx), sqlEx); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (Arrays.binarySearch(this.sqlErrorCodes.getDataAccessResourceFailureCodes(), errorCode) >= 0) { |
|
|
|
else if (Arrays.binarySearch(sqlErrorCodes.getDataAccessResourceFailureCodes(), errorCode) >= 0) { |
|
|
|
logTranslation(task, sql, sqlEx, false); |
|
|
|
logTranslation(task, sql, sqlEx, false); |
|
|
|
return new DataAccessResourceFailureException(buildMessage(task, sql, sqlEx), sqlEx); |
|
|
|
return new DataAccessResourceFailureException(buildMessage(task, sql, sqlEx), sqlEx); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (Arrays.binarySearch(this.sqlErrorCodes.getTransientDataAccessResourceCodes(), errorCode) >= 0) { |
|
|
|
else if (Arrays.binarySearch(sqlErrorCodes.getTransientDataAccessResourceCodes(), errorCode) >= 0) { |
|
|
|
logTranslation(task, sql, sqlEx, false); |
|
|
|
logTranslation(task, sql, sqlEx, false); |
|
|
|
return new TransientDataAccessResourceException(buildMessage(task, sql, sqlEx), sqlEx); |
|
|
|
return new TransientDataAccessResourceException(buildMessage(task, sql, sqlEx), sqlEx); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (Arrays.binarySearch(this.sqlErrorCodes.getCannotAcquireLockCodes(), errorCode) >= 0) { |
|
|
|
else if (Arrays.binarySearch(sqlErrorCodes.getCannotAcquireLockCodes(), errorCode) >= 0) { |
|
|
|
logTranslation(task, sql, sqlEx, false); |
|
|
|
logTranslation(task, sql, sqlEx, false); |
|
|
|
return new CannotAcquireLockException(buildMessage(task, sql, sqlEx), sqlEx); |
|
|
|
return new CannotAcquireLockException(buildMessage(task, sql, sqlEx), sqlEx); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (Arrays.binarySearch(this.sqlErrorCodes.getDeadlockLoserCodes(), errorCode) >= 0) { |
|
|
|
else if (Arrays.binarySearch(sqlErrorCodes.getDeadlockLoserCodes(), errorCode) >= 0) { |
|
|
|
logTranslation(task, sql, sqlEx, false); |
|
|
|
logTranslation(task, sql, sqlEx, false); |
|
|
|
return new DeadlockLoserDataAccessException(buildMessage(task, sql, sqlEx), sqlEx); |
|
|
|
return new DeadlockLoserDataAccessException(buildMessage(task, sql, sqlEx), sqlEx); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (Arrays.binarySearch(this.sqlErrorCodes.getCannotSerializeTransactionCodes(), errorCode) >= 0) { |
|
|
|
else if (Arrays.binarySearch(sqlErrorCodes.getCannotSerializeTransactionCodes(), errorCode) >= 0) { |
|
|
|
logTranslation(task, sql, sqlEx, false); |
|
|
|
logTranslation(task, sql, sqlEx, false); |
|
|
|
return new CannotSerializeTransactionException(buildMessage(task, sql, sqlEx), sqlEx); |
|
|
|
return new CannotSerializeTransactionException(buildMessage(task, sql, sqlEx), sqlEx); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -276,7 +280,7 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep |
|
|
|
// We couldn't identify it more precisely - let's hand it over to the SQLState fallback translator.
|
|
|
|
// We couldn't identify it more precisely - let's hand it over to the SQLState fallback translator.
|
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
String codes; |
|
|
|
String codes; |
|
|
|
if (this.sqlErrorCodes != null && this.sqlErrorCodes.isUseSqlStateForTranslation()) { |
|
|
|
if (sqlErrorCodes != null && sqlErrorCodes.isUseSqlStateForTranslation()) { |
|
|
|
codes = "SQL state '" + sqlEx.getSQLState() + "', error code '" + sqlEx.getErrorCode(); |
|
|
|
codes = "SQL state '" + sqlEx.getSQLState() + "', error code '" + sqlEx.getErrorCode(); |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else { |
|
|
|
|