Browse Source

Check well-known database error codes in case of generic SQL state 23000

Closes gh-29699
pull/29740/head
Juergen Hoeller 2 years ago
parent
commit
a644245e0e
  1. 2
      spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLExceptionSubclassTranslator.java
  2. 17
      spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslator.java
  3. 3
      spring-jdbc/src/test/java/org/springframework/jdbc/support/SQLExceptionSubclassTranslatorTests.java
  4. 21
      spring-jdbc/src/test/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslatorTests.java
  5. 15
      spring-r2dbc/src/main/java/org/springframework/r2dbc/connection/ConnectionFactoryUtils.java
  6. 12
      spring-r2dbc/src/test/java/org/springframework/r2dbc/connection/ConnectionFactoryUtilsUnitTests.java

2
spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLExceptionSubclassTranslator.java

@ -89,7 +89,7 @@ public class SQLExceptionSubclassTranslator extends AbstractFallbackSQLException @@ -89,7 +89,7 @@ public class SQLExceptionSubclassTranslator extends AbstractFallbackSQLException
return new DataIntegrityViolationException(buildMessage(task, sql, ex), ex);
}
if (ex instanceof SQLIntegrityConstraintViolationException) {
if ("23505".equals(ex.getSQLState())) {
if (SQLStateSQLExceptionTranslator.indicatesDuplicateKey(ex.getSQLState(), ex.getErrorCode())) {
return new DuplicateKeyException(buildMessage(task, sql, ex), ex);
}
return new DataIntegrityViolationException(buildMessage(task, sql, ex), ex);

17
spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslator.java

@ -99,7 +99,7 @@ public class SQLStateSQLExceptionTranslator extends AbstractFallbackSQLException @@ -99,7 +99,7 @@ public class SQLStateSQLExceptionTranslator extends AbstractFallbackSQLException
return new BadSqlGrammarException(task, (sql != null ? sql : ""), ex);
}
else if (DATA_INTEGRITY_VIOLATION_CODES.contains(classCode)) {
if ("23505".equals(sqlState)) {
if (indicatesDuplicateKey(sqlState, ex.getErrorCode())) {
return new DuplicateKeyException(buildMessage(task, sql, ex), ex);
}
return new DataIntegrityViolationException(buildMessage(task, sql, ex), ex);
@ -148,4 +148,19 @@ public class SQLStateSQLExceptionTranslator extends AbstractFallbackSQLException @@ -148,4 +148,19 @@ public class SQLStateSQLExceptionTranslator extends AbstractFallbackSQLException
return sqlState;
}
/**
* Check whether the given SQL state (and the associated error code in case
* of a generic SQL state value) indicate a duplicate key exception:
* either SQL state 23505 as a specific indication, or the generic SQL state
* 23000 with well-known vendor codes (1 for Oracle, 1062 for MySQL/MariaDB,
* 2627 for MS SQL Server).
* @param sqlState the SQL state value
* @param errorCode the error code value
*/
static boolean indicatesDuplicateKey(@Nullable String sqlState, int errorCode) {
return ("23505".equals(sqlState) ||
("23000".equals(sqlState) &&
(errorCode == 1 || errorCode == 1062 || errorCode == 2627)));
}
}

3
spring-jdbc/src/test/java/org/springframework/jdbc/support/SQLExceptionSubclassTranslatorTests.java

@ -57,6 +57,9 @@ public class SQLExceptionSubclassTranslatorTests { @@ -57,6 +57,9 @@ public class SQLExceptionSubclassTranslatorTests {
doTest(new SQLFeatureNotSupportedException("", "", 0), InvalidDataAccessApiUsageException.class);
doTest(new SQLIntegrityConstraintViolationException("", "", 0), DataIntegrityViolationException.class);
doTest(new SQLIntegrityConstraintViolationException("", "23505", 0), DuplicateKeyException.class);
doTest(new SQLIntegrityConstraintViolationException("", "23000", 1), DuplicateKeyException.class);
doTest(new SQLIntegrityConstraintViolationException("", "23000", 1062), DuplicateKeyException.class);
doTest(new SQLIntegrityConstraintViolationException("", "23505", 2627), DuplicateKeyException.class);
doTest(new SQLInvalidAuthorizationSpecException("", "", 0), PermissionDeniedDataAccessException.class);
doTest(new SQLNonTransientConnectionException("", "", 0), DataAccessResourceFailureException.class);
doTest(new SQLRecoverableException("", "", 0), RecoverableDataAccessException.class);

21
spring-jdbc/src/test/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslatorTests.java

@ -61,6 +61,21 @@ public class SQLStateSQLExceptionTranslatorTests { @@ -61,6 +61,21 @@ public class SQLStateSQLExceptionTranslatorTests {
doTest("23505", DuplicateKeyException.class);
}
@Test
public void translateDuplicateKeyOracle() {
doTest("23000", 1, DuplicateKeyException.class);
}
@Test
public void translateDuplicateKeyMySQL() {
doTest("23000", 1062, DuplicateKeyException.class);
}
@Test
public void translateDuplicateKeyMSSQL() {
doTest("23000", 2627, DuplicateKeyException.class);
}
@Test
public void translateDataAccessResourceFailure() {
doTest("53", DataAccessResourceFailureException.class);
@ -105,8 +120,12 @@ public class SQLStateSQLExceptionTranslatorTests { @@ -105,8 +120,12 @@ public class SQLStateSQLExceptionTranslatorTests {
private void doTest(@Nullable String sqlState, @Nullable Class<?> dataAccessExceptionType) {
doTest(sqlState, 0, dataAccessExceptionType);
}
private void doTest(@Nullable String sqlState, int errorCode, @Nullable Class<?> dataAccessExceptionType) {
SQLExceptionTranslator translator = new SQLStateSQLExceptionTranslator();
SQLException ex = new SQLException("reason", sqlState);
SQLException ex = new SQLException("reason", sqlState, errorCode);
DataAccessException dax = translator.translate("task", "SQL", ex);
if (dataAccessExceptionType == null) {

15
spring-r2dbc/src/main/java/org/springframework/r2dbc/connection/ConnectionFactoryUtils.java

@ -231,7 +231,7 @@ public abstract class ConnectionFactoryUtils { @@ -231,7 +231,7 @@ public abstract class ConnectionFactoryUtils {
return new DataAccessResourceFailureException(buildMessage(task, sql, ex), ex);
}
if (ex instanceof R2dbcDataIntegrityViolationException) {
if ("23505".equals(ex.getSqlState())) {
if (indicatesDuplicateKey(ex.getSqlState(), ex.getErrorCode())) {
return new DuplicateKeyException(buildMessage(task, sql, ex), ex);
}
return new DataIntegrityViolationException(buildMessage(task, sql, ex), ex);
@ -246,6 +246,19 @@ public abstract class ConnectionFactoryUtils { @@ -246,6 +246,19 @@ public abstract class ConnectionFactoryUtils {
return new UncategorizedR2dbcException(buildMessage(task, sql, ex), sql, ex);
}
/**
* Check whether the given SQL state (and the associated error code in case
* of a generic SQL state value) indicate a duplicate key exception. See
* {@code org.springframework.jdbc.support.SQLStateSQLExceptionTranslator#indicatesDuplicateKey}.
* @param sqlState the SQL state value
* @param errorCode the error code value
*/
static boolean indicatesDuplicateKey(@Nullable String sqlState, int errorCode) {
return ("23505".equals(sqlState) ||
("23000".equals(sqlState) &&
(errorCode == 1 || errorCode == 1062 || errorCode == 2627)));
}
/**
* Build a message {@code String} for the given {@link R2dbcException}.
* <p>To be called by translator subclasses when creating an instance of a generic

12
spring-r2dbc/src/test/java/org/springframework/r2dbc/connection/ConnectionFactoryUtilsUnitTests.java

@ -95,6 +95,18 @@ public class ConnectionFactoryUtilsUnitTests { @@ -95,6 +95,18 @@ public class ConnectionFactoryUtilsUnitTests {
exception = ConnectionFactoryUtils.convertR2dbcException("", "",
new R2dbcDataIntegrityViolationException("reason", "23505"));
assertThat(exception).isExactlyInstanceOf(DuplicateKeyException.class);
exception = ConnectionFactoryUtils.convertR2dbcException("", "",
new R2dbcDataIntegrityViolationException("reason", "23000", 1));
assertThat(exception).isExactlyInstanceOf(DuplicateKeyException.class);
exception = ConnectionFactoryUtils.convertR2dbcException("", "",
new R2dbcDataIntegrityViolationException("reason", "23000", 1062));
assertThat(exception).isExactlyInstanceOf(DuplicateKeyException.class);
exception = ConnectionFactoryUtils.convertR2dbcException("", "",
new R2dbcDataIntegrityViolationException("reason", "23000", 2627));
assertThat(exception).isExactlyInstanceOf(DuplicateKeyException.class);
}
@Test

Loading…
Cancel
Save