diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslator.java b/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslator.java index 87b2b6f0a25..13b2f43d0c2 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslator.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslator.java @@ -95,7 +95,9 @@ public class SQLStateSQLExceptionTranslator extends AbstractFallbackSQLException 301, // SAP HANA 1062, // MySQL/MariaDB 2601, // MS SQL Server - 2627 // MS SQL Server + 2627, // MS SQL Server + -239, // Informix + -268 // Informix ); diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslatorTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslatorTests.java index 71820d7259d..1bce4f41c03 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslatorTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/support/SQLStateSQLExceptionTranslatorTests.java @@ -90,6 +90,16 @@ class SQLStateSQLExceptionTranslatorTests { assertTranslation("23000", 301, DuplicateKeyException.class); } + @Test + void translateDuplicateKeyInformix1() { + assertTranslation("23000", -239, DuplicateKeyException.class); + } + + @Test + void translateDuplicateKeyInformix2() { + assertTranslation("23000", -268, DuplicateKeyException.class); + } + @Test void translateDataAccessResourceFailure() { assertTranslation("53", DataAccessResourceFailureException.class); diff --git a/spring-r2dbc/src/main/java/org/springframework/r2dbc/connection/ConnectionFactoryUtils.java b/spring-r2dbc/src/main/java/org/springframework/r2dbc/connection/ConnectionFactoryUtils.java index 162d5c2e6c5..5a8cc34992f 100644 --- a/spring-r2dbc/src/main/java/org/springframework/r2dbc/connection/ConnectionFactoryUtils.java +++ b/spring-r2dbc/src/main/java/org/springframework/r2dbc/connection/ConnectionFactoryUtils.java @@ -76,7 +76,9 @@ public abstract class ConnectionFactoryUtils { 301, // SAP HANA 1062, // MySQL/MariaDB 2601, // MS SQL Server - 2627 // MS SQL Server + 2627, // MS SQL Server + -239, // Informix + -268 // Informix ); diff --git a/spring-r2dbc/src/test/java/org/springframework/r2dbc/connection/ConnectionFactoryUtilsTests.java b/spring-r2dbc/src/test/java/org/springframework/r2dbc/connection/ConnectionFactoryUtilsTests.java index 49861f0a4e8..cd4fb8c91d8 100644 --- a/spring-r2dbc/src/test/java/org/springframework/r2dbc/connection/ConnectionFactoryUtilsTests.java +++ b/spring-r2dbc/src/test/java/org/springframework/r2dbc/connection/ConnectionFactoryUtilsTests.java @@ -16,6 +16,8 @@ package org.springframework.r2dbc.connection; +import java.util.List; + import io.r2dbc.spi.R2dbcBadGrammarException; import io.r2dbc.spi.R2dbcDataIntegrityViolationException; import io.r2dbc.spi.R2dbcException; @@ -25,6 +27,9 @@ import io.r2dbc.spi.R2dbcRollbackException; import io.r2dbc.spi.R2dbcTimeoutException; import io.r2dbc.spi.R2dbcTransientResourceException; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.FieldSource; import org.springframework.dao.CannotAcquireLockException; import org.springframework.dao.DataAccessResourceFailureException; @@ -38,6 +43,7 @@ import org.springframework.r2dbc.BadSqlGrammarException; import org.springframework.r2dbc.UncategorizedR2dbcException; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.params.provider.Arguments.arguments; /** * Tests for {@link ConnectionFactoryUtils}. @@ -91,30 +97,25 @@ class ConnectionFactoryUtilsTests { Exception exception = ConnectionFactoryUtils.convertR2dbcException("", "", new R2dbcDataIntegrityViolationException()); assertThat(exception).isExactlyInstanceOf(DataIntegrityViolationException.class); + } - exception = ConnectionFactoryUtils.convertR2dbcException("", "", - new R2dbcDataIntegrityViolationException("reason", "23505")); - assertThat(exception).isExactlyInstanceOf(DuplicateKeyException.class); + static final List duplicateKeyErrorCodes = List.of( + arguments("Oracle", "23505", 0), + arguments("Oracle", "23000", 1), + arguments("SAP HANA", "23000", 301), + arguments("MySQL/MariaDB", "23000", 1062), + arguments("MS SQL Server", "23000", 2601), + arguments("MS SQL Server", "23000", 2627), + arguments("Informix", "23000", -239), + arguments("Informix", "23000", -268) + ); - exception = ConnectionFactoryUtils.convertR2dbcException("", "", - new R2dbcDataIntegrityViolationException("reason", "23000", 1)); - assertThat(exception).as("Oracle").isExactlyInstanceOf(DuplicateKeyException.class); - - exception = ConnectionFactoryUtils.convertR2dbcException("", "", - new R2dbcDataIntegrityViolationException("reason", "23000", 301)); - assertThat(exception).as("SAP HANA").isExactlyInstanceOf(DuplicateKeyException.class); - - exception = ConnectionFactoryUtils.convertR2dbcException("", "", - new R2dbcDataIntegrityViolationException("reason", "23000", 1062)); - assertThat(exception).as("MySQL/MariaDB").isExactlyInstanceOf(DuplicateKeyException.class); - - exception = ConnectionFactoryUtils.convertR2dbcException("", "", - new R2dbcDataIntegrityViolationException("reason", "23000", 2601)); - assertThat(exception).as("MS SQL Server").isExactlyInstanceOf(DuplicateKeyException.class); - - exception = ConnectionFactoryUtils.convertR2dbcException("", "", - new R2dbcDataIntegrityViolationException("reason", "23000", 2627)); - assertThat(exception).as("MS SQL Server").isExactlyInstanceOf(DuplicateKeyException.class); + @ParameterizedTest + @FieldSource("duplicateKeyErrorCodes") + void shouldTranslateIntegrityViolationExceptionToDuplicateKeyException(String db, String sqlState, int errorCode) { + Exception exception = ConnectionFactoryUtils.convertR2dbcException("", "", + new R2dbcDataIntegrityViolationException("reason", sqlState, errorCode)); + assertThat(exception).as(db).isExactlyInstanceOf(DuplicateKeyException.class); } @Test @@ -135,24 +136,27 @@ class ConnectionFactoryUtilsTests { void messageGeneration() { Exception exception = ConnectionFactoryUtils.convertR2dbcException("TASK", "SOME-SQL", new R2dbcTransientResourceException("MESSAGE")); - assertThat(exception).isExactlyInstanceOf( - TransientDataAccessResourceException.class).hasMessage("TASK; SQL [SOME-SQL]; MESSAGE"); + assertThat(exception) + .isExactlyInstanceOf(TransientDataAccessResourceException.class) + .hasMessage("TASK; SQL [SOME-SQL]; MESSAGE"); } @Test void messageGenerationNullSQL() { Exception exception = ConnectionFactoryUtils.convertR2dbcException("TASK", null, new R2dbcTransientResourceException("MESSAGE")); - assertThat(exception).isExactlyInstanceOf( - TransientDataAccessResourceException.class).hasMessage("TASK; MESSAGE"); + assertThat(exception) + .isExactlyInstanceOf(TransientDataAccessResourceException.class) + .hasMessage("TASK; MESSAGE"); } @Test void messageGenerationNullMessage() { Exception exception = ConnectionFactoryUtils.convertR2dbcException("TASK", "SOME-SQL", new R2dbcTransientResourceException()); - assertThat(exception).isExactlyInstanceOf( - TransientDataAccessResourceException.class).hasMessage("TASK; SQL [SOME-SQL]; null"); + assertThat(exception) + .isExactlyInstanceOf(TransientDataAccessResourceException.class) + .hasMessage("TASK; SQL [SOME-SQL]; null"); }