Drop SQLExceptionSubclassFactory and unify SQLStateSQLExceptionTranslator tests

This commit is contained in:
Juergen Hoeller
2022-12-13 12:05:35 +01:00
parent 8c80ec1138
commit 064c618050
5 changed files with 84 additions and 248 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package org.springframework.jdbc.support;
import java.sql.SQLDataException;
import java.sql.SQLException;
import org.junit.jupiter.api.Test;
@@ -37,8 +38,8 @@ public class SQLExceptionCustomTranslatorTests {
private static SQLErrorCodes ERROR_CODES = new SQLErrorCodes();
static {
ERROR_CODES.setBadSqlGrammarCodes(new String[] { "1" });
ERROR_CODES.setDataAccessResourceFailureCodes(new String[] { "2" });
ERROR_CODES.setBadSqlGrammarCodes("1");
ERROR_CODES.setDataAccessResourceFailureCodes("2");
ERROR_CODES.setCustomSqlExceptionTranslatorClass(CustomSqlExceptionTranslator.class);
}
@@ -47,7 +48,7 @@ public class SQLExceptionCustomTranslatorTests {
@Test
public void badSqlGrammarException() {
SQLException badSqlGrammarExceptionEx = SQLExceptionSubclassFactory.newSQLDataException("", "", 1);
SQLException badSqlGrammarExceptionEx = new SQLDataException("", "", 1);
DataAccessException dae = sext.translate("task", "SQL", badSqlGrammarExceptionEx);
assertThat(dae.getCause()).isEqualTo(badSqlGrammarExceptionEx);
assertThat(dae).isInstanceOf(BadSqlGrammarException.class);
@@ -55,7 +56,7 @@ public class SQLExceptionCustomTranslatorTests {
@Test
public void dataAccessResourceException() {
SQLException dataAccessResourceEx = SQLExceptionSubclassFactory.newSQLDataException("", "", 2);
SQLException dataAccessResourceEx = new SQLDataException("", "", 2);
DataAccessException dae = sext.translate("task", "SQL", dataAccessResourceEx);
assertThat(dae.getCause()).isEqualTo(dataAccessResourceEx);
assertThat(dae).isInstanceOf(TransientDataAccessResourceException.class);

View File

@@ -1,78 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.support;
import java.sql.SQLDataException;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.sql.SQLInvalidAuthorizationSpecException;
import java.sql.SQLNonTransientConnectionException;
import java.sql.SQLRecoverableException;
import java.sql.SQLSyntaxErrorException;
import java.sql.SQLTimeoutException;
import java.sql.SQLTransactionRollbackException;
import java.sql.SQLTransientConnectionException;
/**
* Class to generate {@link SQLException} subclasses for testing purposes.
*
* @author Thomas Risberg
*/
public class SQLExceptionSubclassFactory {
public static SQLException newSQLDataException(String reason, String SQLState, int vendorCode) {
return new SQLDataException(reason, SQLState, vendorCode);
}
public static SQLException newSQLFeatureNotSupportedException(String reason, String SQLState, int vendorCode) {
return new SQLFeatureNotSupportedException(reason, SQLState, vendorCode);
}
public static SQLException newSQLIntegrityConstraintViolationException(String reason, String SQLState, int vendorCode) {
return new SQLIntegrityConstraintViolationException(reason, SQLState, vendorCode);
}
public static SQLException newSQLInvalidAuthorizationSpecException(String reason, String SQLState, int vendorCode) {
return new SQLInvalidAuthorizationSpecException(reason, SQLState, vendorCode);
}
public static SQLException newSQLNonTransientConnectionException(String reason, String SQLState, int vendorCode) {
return new SQLNonTransientConnectionException(reason, SQLState, vendorCode);
}
public static SQLException newSQLSyntaxErrorException(String reason, String SQLState, int vendorCode) {
return new SQLSyntaxErrorException(reason, SQLState, vendorCode);
}
public static SQLException newSQLTransactionRollbackException(String reason, String SQLState, int vendorCode) {
return new SQLTransactionRollbackException(reason, SQLState, vendorCode);
}
public static SQLException newSQLTransientConnectionException(String reason, String SQLState, int vendorCode) {
return new SQLTransientConnectionException(reason, SQLState, vendorCode);
}
public static SQLException newSQLTimeoutException(String reason, String SQLState, int vendorCode) {
return new SQLTimeoutException(reason, SQLState, vendorCode);
}
public static SQLException newSQLRecoverableException(String reason, String SQLState, int vendorCode) {
return new SQLRecoverableException(reason, SQLState, vendorCode);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,11 +16,22 @@
package org.springframework.jdbc.support;
import java.sql.SQLDataException;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.sql.SQLInvalidAuthorizationSpecException;
import java.sql.SQLNonTransientConnectionException;
import java.sql.SQLRecoverableException;
import java.sql.SQLSyntaxErrorException;
import java.sql.SQLTimeoutException;
import java.sql.SQLTransactionRollbackException;
import java.sql.SQLTransientConnectionException;
import org.junit.jupiter.api.Test;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
@@ -34,78 +45,41 @@ import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Thomas Risberg
* @author Juergen Hoeller
*/
public class SQLExceptionSubclassTranslatorTests {
private static SQLErrorCodes ERROR_CODES = new SQLErrorCodes();
@Test
public void exceptionClassTranslation() {
doTest(new SQLDataException("", "", 0), DataIntegrityViolationException.class);
doTest(new SQLFeatureNotSupportedException("", "", 0), InvalidDataAccessApiUsageException.class);
doTest(new SQLIntegrityConstraintViolationException("", "", 0), DataIntegrityViolationException.class);
doTest(new SQLInvalidAuthorizationSpecException("", "", 0), PermissionDeniedDataAccessException.class);
doTest(new SQLNonTransientConnectionException("", "", 0), DataAccessResourceFailureException.class);
doTest(new SQLRecoverableException("", "", 0), RecoverableDataAccessException.class);
doTest(new SQLSyntaxErrorException("", "", 0), BadSqlGrammarException.class);
doTest(new SQLTimeoutException("", "", 0), QueryTimeoutException.class);
doTest(new SQLTransactionRollbackException("", "", 0), ConcurrencyFailureException.class);
doTest(new SQLTransientConnectionException("", "", 0), TransientDataAccessResourceException.class);
}
static {
ERROR_CODES.setBadSqlGrammarCodes("1");
@Test
public void fallbackStateTranslation() {
// Test fallback. We assume that no database will ever return this error code,
// but 07xxx will be bad grammar picked up by the fallback SQLState translator
doTest(new SQLException("", "07xxx", 666666666), BadSqlGrammarException.class);
// and 08xxx will be data resource failure (non-transient) picked up by the fallback SQLState translator
doTest(new SQLException("", "08xxx", 666666666), DataAccessResourceFailureException.class);
}
@Test
public void errorCodeTranslation() {
SQLExceptionTranslator sext = new SQLErrorCodeSQLExceptionTranslator(ERROR_CODES);
private void doTest(SQLException ex, Class<?> dataAccessExceptionType) {
SQLExceptionTranslator translator = new SQLExceptionSubclassTranslator();
DataAccessException dax = translator.translate("task", "SQL", ex);
SQLException dataIntegrityViolationEx = SQLExceptionSubclassFactory.newSQLDataException("", "", 0);
DataIntegrityViolationException divex = (DataIntegrityViolationException) sext.translate("task", "SQL", dataIntegrityViolationEx);
assertThat(divex.getCause()).isEqualTo(dataIntegrityViolationEx);
SQLException featureNotSupEx = SQLExceptionSubclassFactory.newSQLFeatureNotSupportedException("", "", 0);
InvalidDataAccessApiUsageException idaex = (InvalidDataAccessApiUsageException) sext.translate("task", "SQL", featureNotSupEx);
assertThat(idaex.getCause()).isEqualTo(featureNotSupEx);
SQLException dataIntegrityViolationEx2 = SQLExceptionSubclassFactory.newSQLIntegrityConstraintViolationException("", "", 0);
DataIntegrityViolationException divex2 = (DataIntegrityViolationException) sext.translate("task", "SQL", dataIntegrityViolationEx2);
assertThat(divex2.getCause()).isEqualTo(dataIntegrityViolationEx2);
SQLException permissionDeniedEx = SQLExceptionSubclassFactory.newSQLInvalidAuthorizationSpecException("", "", 0);
PermissionDeniedDataAccessException pdaex = (PermissionDeniedDataAccessException) sext.translate("task", "SQL", permissionDeniedEx);
assertThat(pdaex.getCause()).isEqualTo(permissionDeniedEx);
SQLException dataAccessResourceEx = SQLExceptionSubclassFactory.newSQLNonTransientConnectionException("", "", 0);
DataAccessResourceFailureException darex = (DataAccessResourceFailureException) sext.translate("task", "SQL", dataAccessResourceEx);
assertThat(darex.getCause()).isEqualTo(dataAccessResourceEx);
SQLException badSqlEx2 = SQLExceptionSubclassFactory.newSQLSyntaxErrorException("", "", 0);
BadSqlGrammarException bsgex2 = (BadSqlGrammarException) sext.translate("task", "SQL2", badSqlEx2);
assertThat(bsgex2.getSql()).isEqualTo("SQL2");
assertThat((Object) bsgex2.getSQLException()).isEqualTo(badSqlEx2);
SQLException tranRollbackEx = SQLExceptionSubclassFactory.newSQLTransactionRollbackException("", "", 0);
ConcurrencyFailureException cfex = (ConcurrencyFailureException) sext.translate("task", "SQL", tranRollbackEx);
assertThat(cfex.getCause()).isEqualTo(tranRollbackEx);
SQLException transientConnEx = SQLExceptionSubclassFactory.newSQLTransientConnectionException("", "", 0);
TransientDataAccessResourceException tdarex = (TransientDataAccessResourceException) sext.translate("task", "SQL", transientConnEx);
assertThat(tdarex.getCause()).isEqualTo(transientConnEx);
SQLException transientConnEx2 = SQLExceptionSubclassFactory.newSQLTimeoutException("", "", 0);
QueryTimeoutException tdarex2 = (QueryTimeoutException) sext.translate("task", "SQL", transientConnEx2);
assertThat(tdarex2.getCause()).isEqualTo(transientConnEx2);
SQLException recoverableEx = SQLExceptionSubclassFactory.newSQLRecoverableException("", "", 0);
RecoverableDataAccessException rdaex2 = (RecoverableDataAccessException) sext.translate("task", "SQL", recoverableEx);
assertThat(rdaex2.getCause()).isEqualTo(recoverableEx);
// Test classic error code translation. We should move there next if the exception we pass in is not one
// of the new subclasses.
SQLException sexEct = new SQLException("", "", 1);
BadSqlGrammarException bsgEct = (BadSqlGrammarException) sext.translate("task", "SQL-ECT", sexEct);
assertThat(bsgEct.getSql()).isEqualTo("SQL-ECT");
assertThat((Object) bsgEct.getSQLException()).isEqualTo(sexEct);
// Test fallback. We assume that no database will ever return this error code,
// but 07xxx will be bad grammar picked up by the fallback SQLState translator
SQLException sexFbt = new SQLException("", "07xxx", 666666666);
BadSqlGrammarException bsgFbt = (BadSqlGrammarException) sext.translate("task", "SQL-FBT", sexFbt);
assertThat(bsgFbt.getSql()).isEqualTo("SQL-FBT");
assertThat((Object) bsgFbt.getSQLException()).isEqualTo(sexFbt);
// and 08xxx will be data resource failure (non-transient) picked up by the fallback SQLState translator
SQLException sexFbt2 = new SQLException("", "08xxx", 666666666);
DataAccessResourceFailureException darfFbt = (DataAccessResourceFailureException) sext.translate("task", "SQL-FBT2", sexFbt2);
assertThat(darfFbt.getCause()).isEqualTo(sexFbt2);
assertThat(dax).as("Specific translation must not result in null").isNotNull();
assertThat(dax).as("Wrong DataAccessException type returned").isExactlyInstanceOf(dataAccessExceptionType);
assertThat(dax.getCause()).as("The exact same original SQLException must be preserved").isSameAs(ex);
}
}

View File

@@ -1,77 +0,0 @@
/*
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.support;
import java.sql.SQLException;
import org.junit.jupiter.api.Test;
import org.springframework.jdbc.BadSqlGrammarException;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Rod Johnson
* @since 13-Jan-03
*/
public class SQLStateExceptionTranslatorTests {
private static final String sql = "SELECT FOO FROM BAR";
private final SQLStateSQLExceptionTranslator trans = new SQLStateSQLExceptionTranslator();
// ALSO CHECK CHAIN of SQLExceptions!?
// also allow chain of translators? default if can't do specific?
@Test
public void badSqlGrammar() {
SQLException sex = new SQLException("Message", "42001", 1);
try {
throw this.trans.translate("task", sql, sex);
}
catch (BadSqlGrammarException ex) {
// OK
assertThat(sql.equals(ex.getSql())).as("SQL is correct").isTrue();
assertThat(sex.equals(ex.getSQLException())).as("Exception matches").isTrue();
}
}
@Test
public void invalidSqlStateCode() {
SQLException sex = new SQLException("Message", "NO SUCH CODE", 1);
assertThat(this.trans.translate("task", sql, sex)).isNull();
}
/**
* PostgreSQL can return null.
* SAP DB can apparently return empty SQL code.
* Bug 729170
*/
@Test
public void malformedSqlStateCodes() {
SQLException sex = new SQLException("Message", null, 1);
assertThat(this.trans.translate("task", sql, sex)).isNull();
sex = new SQLException("Message", "", 1);
assertThat(this.trans.translate("task", sql, sex)).isNull();
// One char's not allowed
sex = new SQLException("Message", "I", 1);
assertThat(this.trans.translate("task", sql, sex)).isNull();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@ import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.TransientDataAccessResourceException;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.lang.Nullable;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
@@ -37,58 +38,73 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
*/
public class SQLStateSQLExceptionTranslatorTests {
private static final String REASON = "The game is afoot!";
private static final String TASK = "Counting sheep... yawn.";
private static final String SQL = "select count(0) from t_sheep where over_fence = ... yawn... 1";
@Test
public void testTranslateNullException() {
public void translateNullException() {
assertThatIllegalArgumentException().isThrownBy(() ->
new SQLStateSQLExceptionTranslator().translate("", "", null));
}
@Test
public void testTranslateBadSqlGrammar() {
public void translateBadSqlGrammar() {
doTest("07", BadSqlGrammarException.class);
}
@Test
public void testTranslateDataIntegrityViolation() {
public void translateDataIntegrityViolation() {
doTest("23", DataIntegrityViolationException.class);
}
@Test
public void testTranslateDataAccessResourceFailure() {
public void translateDataAccessResourceFailure() {
doTest("53", DataAccessResourceFailureException.class);
}
@Test
public void testTranslateTransientDataAccessResourceFailure() {
public void translateTransientDataAccessResourceFailure() {
doTest("S1", TransientDataAccessResourceException.class);
}
@Test
public void testTranslateConcurrencyFailure() {
public void translateConcurrencyFailure() {
doTest("40", ConcurrencyFailureException.class);
}
@Test
public void testTranslateUncategorized() {
assertThat(new SQLStateSQLExceptionTranslator().translate("", "", new SQLException(REASON, "00000000"))).isNull();
public void translateUncategorized() {
doTest("00000000", null);
}
@Test
public void invalidSqlStateCode() {
doTest("NO SUCH CODE", null);
}
/**
* PostgreSQL can return null.
* SAP DB can apparently return empty SQL code.
* Bug 729170
*/
@Test
public void malformedSqlStateCodes() {
doTest(null, null);
doTest("", null);
doTest("I", null);
}
private void doTest(String sqlState, Class<?> dataAccessExceptionType) {
SQLException ex = new SQLException(REASON, sqlState);
private void doTest(@Nullable String sqlState, @Nullable Class<?> dataAccessExceptionType) {
SQLExceptionTranslator translator = new SQLStateSQLExceptionTranslator();
DataAccessException dax = translator.translate(TASK, SQL, ex);
assertThat(dax).as("Specific translation must not result in a null DataAccessException being returned.").isNotNull();
assertThat(dax.getClass()).as("Wrong DataAccessException type returned as the result of the translation").isEqualTo(dataAccessExceptionType);
assertThat(dax.getCause()).as("The original SQLException must be preserved in the translated DataAccessException").isNotNull();
assertThat(dax.getCause()).as("The exact same original SQLException must be preserved in the translated DataAccessException").isSameAs(ex);
SQLException ex = new SQLException("reason", sqlState);
DataAccessException dax = translator.translate("task", "SQL", ex);
if (dataAccessExceptionType == null) {
assertThat(dax).as("Expected translation to null").isNull();
return;
}
assertThat(dax).as("Specific translation must not result in null").isNotNull();
assertThat(dax).as("Wrong DataAccessException type returned").isExactlyInstanceOf(dataAccessExceptionType);
assertThat(dax.getCause()).as("The exact same original SQLException must be preserved").isSameAs(ex);
}
}