From 8a4ce142c42375486542054b00c82331fecac311 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 6 Feb 2013 21:32:42 +0100 Subject: [PATCH] SQLErrorCodeSQLExceptionTranslator tries to find SQLException with actual error code, looping through the causes. Issue: SPR-10260 --- .../SQLErrorCodeSQLExceptionTranslator.java | 26 +++++++++++-------- ...LErrorCodeSQLExceptionTranslatorTests.java | 20 ++++++++++---- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodeSQLExceptionTranslator.java b/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodeSQLExceptionTranslator.java index a3290b8825..3bf52d3f71 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodeSQLExceptionTranslator.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodeSQLExceptionTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -20,7 +20,6 @@ import java.lang.reflect.Constructor; import java.sql.BatchUpdateException; import java.sql.SQLException; import java.util.Arrays; - import javax.sql.DataSource; import org.springframework.core.JdkVersion; @@ -30,9 +29,9 @@ import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.DeadlockLoserDataAccessException; +import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.PermissionDeniedDataAccessException; import org.springframework.dao.TransientDataAccessResourceException; -import org.springframework.dao.DuplicateKeyException; import org.springframework.jdbc.BadSqlGrammarException; import org.springframework.jdbc.InvalidResultSetAccessException; @@ -201,20 +200,25 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep // Check SQLErrorCodes with corresponding error code, if available. if (this.sqlErrorCodes != null) { - String errorCode = null; + String errorCode; if (this.sqlErrorCodes.isUseSqlStateForTranslation()) { errorCode = sqlEx.getSQLState(); } else { - errorCode = Integer.toString(sqlEx.getErrorCode()); + // Try to find SQLException with actual error code, looping through the causes. + // E.g. applicable to java.sql.DataTruncation as of JDK 1.6. + SQLException current = sqlEx; + while (current.getErrorCode() == 0 && current.getCause() instanceof SQLException) { + current = (SQLException) current.getCause(); + } + errorCode = Integer.toString(current.getErrorCode()); } if (errorCode != null) { // Look for defined custom translations first. CustomSQLErrorCodesTranslation[] customTranslations = this.sqlErrorCodes.getCustomTranslations(); if (customTranslations != null) { - for (int i = 0; i < customTranslations.length; i++) { - CustomSQLErrorCodesTranslation customTranslation = customTranslations[i]; + for (CustomSQLErrorCodesTranslation customTranslation : customTranslations) { if (Arrays.binarySearch(customTranslation.getErrorCodes(), errorCode) >= 0) { if (customTranslation.getExceptionClass() != null) { DataAccessException customException = createCustomException( @@ -273,7 +277,7 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep // We couldn't identify it more precisely - let's hand it over to the SQLState fallback translator. if (logger.isDebugEnabled()) { - String codes = null; + String codes; if (this.sqlErrorCodes != null && this.sqlErrorCodes.isUseSqlStateForTranslation()) { codes = "SQL state '" + sqlEx.getSQLState() + "', error code '" + sqlEx.getErrorCode(); } @@ -321,8 +325,8 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep try { int constructorType = 0; Constructor[] constructors = exceptionClass.getConstructors(); - for (int i = 0; i < constructors.length; i++) { - Class[] parameterTypes = constructors[i].getParameterTypes(); + for (Constructor constructor : constructors) { + Class[] parameterTypes = constructor.getParameterTypes(); if (parameterTypes.length == 1 && parameterTypes[0].equals(String.class)) { if (constructorType < MESSAGE_ONLY_CONSTRUCTOR) constructorType = MESSAGE_ONLY_CONSTRUCTOR; @@ -350,7 +354,7 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep } // invoke constructor - Constructor exceptionConstructor = null; + Constructor exceptionConstructor; switch (constructorType) { case MESSAGE_SQL_SQLEX_CONSTRUCTOR: Class[] messageAndSqlAndSqlExArgsClass = new Class[] {String.class, String.class, SQLException.class}; diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/support/SQLErrorCodeSQLExceptionTranslatorTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/support/SQLErrorCodeSQLExceptionTranslatorTests.java index c2e07ec765..f7a23fa2e0 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/support/SQLErrorCodeSQLExceptionTranslatorTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/support/SQLErrorCodeSQLExceptionTranslatorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,8 +16,9 @@ package org.springframework.jdbc.support; -import java.sql.SQLException; import java.sql.BatchUpdateException; +import java.sql.DataTruncation; +import java.sql.SQLException; import junit.framework.TestCase; @@ -33,6 +34,7 @@ import org.springframework.jdbc.InvalidResultSetAccessException; /** * @author Rod Johnson + * @author Juergen Hoeller */ public class SQLErrorCodeSQLExceptionTranslatorTests extends TestCase { @@ -92,13 +94,21 @@ public class SQLErrorCodeSQLExceptionTranslatorTests extends TestCase { SQLExceptionTranslator sext = new SQLErrorCodeSQLExceptionTranslator(ERROR_CODES); SQLException badSqlEx = new SQLException("", "", 1); - BatchUpdateException batchUdateEx = new BatchUpdateException(); - batchUdateEx.setNextException(badSqlEx); - BadSqlGrammarException bsgex = (BadSqlGrammarException) sext.translate("task", "SQL", batchUdateEx); + BatchUpdateException batchUpdateEx = new BatchUpdateException(); + batchUpdateEx.setNextException(badSqlEx); + BadSqlGrammarException bsgex = (BadSqlGrammarException) sext.translate("task", "SQL", batchUpdateEx); assertEquals("SQL", bsgex.getSql()); assertEquals(badSqlEx, bsgex.getSQLException()); } + public void testDataTruncationTranslation() { + SQLExceptionTranslator sext = new SQLErrorCodeSQLExceptionTranslator(ERROR_CODES); + + SQLException dataAccessEx = new SQLException("", "", 5); + DataTruncation dataTruncation = new DataTruncation(1, true, true, 1, 1, dataAccessEx); + DataAccessResourceFailureException daex = (DataAccessResourceFailureException) sext.translate("task", "SQL", dataTruncation); + assertEquals(dataTruncation, daex.getCause()); + } @SuppressWarnings("serial") public void testCustomTranslateMethodTranslation() {