1. /*
  2. * Copyright 2002-2004 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.springframework.jdbc.support;
  17. import java.sql.SQLException;
  18. import java.util.HashSet;
  19. import java.util.Set;
  20. import org.apache.commons.logging.Log;
  21. import org.apache.commons.logging.LogFactory;
  22. import org.springframework.dao.DataAccessException;
  23. import org.springframework.dao.DataIntegrityViolationException;
  24. import org.springframework.jdbc.BadSqlGrammarException;
  25. import org.springframework.jdbc.UncategorizedSQLException;
  26. /**
  27. * Implementation of SQLExceptionTranslator that uses the SQLState
  28. * code in the SQLException. Can't diagnose all problems, but is
  29. * portable between databases.
  30. * @author Rod Johnson
  31. * @version $Id: SQLStateSQLExceptionTranslator.java,v 1.4 2004/03/18 02:46:15 trisberg Exp $
  32. * @see java.sql.SQLException#getSQLState
  33. */
  34. public class SQLStateSQLExceptionTranslator implements SQLExceptionTranslator {
  35. protected final Log logger = LogFactory.getLog(getClass());
  36. /** Set of String 2-digit codes that indicate bad SQL */
  37. private static Set BAD_SQL_CODES = new HashSet();
  38. /** Set of String 2-digit codes that indicate RDBMS integrity violation */
  39. private static Set INTEGRITY_VIOLATION_CODES = new HashSet();
  40. // Populate reference data
  41. static {
  42. BAD_SQL_CODES.add("07");
  43. BAD_SQL_CODES.add("42");
  44. BAD_SQL_CODES.add("65"); // Oracle throws on unknown identifier
  45. BAD_SQL_CODES.add("S0"); // MySQL uses this - from ODBC error codes?
  46. INTEGRITY_VIOLATION_CODES.add("22"); // Integrity constraint violation
  47. INTEGRITY_VIOLATION_CODES.add("23"); // Integrity constraint violation
  48. INTEGRITY_VIOLATION_CODES.add("27"); // Triggered data change violation
  49. INTEGRITY_VIOLATION_CODES.add("44"); // With check violation
  50. }
  51. public DataAccessException translate(String task, String sql, SQLException sqlex) {
  52. if (logger.isInfoEnabled()) {
  53. logger.info("Translating SQLException with SQLState '" + sqlex.getSQLState() +
  54. "' and errorCode '" + sqlex.getErrorCode() + "' and message [" +
  55. sqlex.getMessage() + "]; SQL was [" + sql + "] for task [" + task + "]");
  56. }
  57. String sqlState = sqlex.getSQLState();
  58. // some JDBC drivers nest the actual exception from a batched update - need to get the nested one
  59. if (sqlState == null) {
  60. SQLException nestedEx = sqlex.getNextException();
  61. if (nestedEx != null)
  62. sqlState = nestedEx.getSQLState();
  63. }
  64. if (sqlState != null && sqlState.length() >= 2) {
  65. String classCode = sqlState.substring(0, 2);
  66. if (BAD_SQL_CODES.contains(classCode)) {
  67. return new BadSqlGrammarException(task, sql, sqlex);
  68. }
  69. else if (INTEGRITY_VIOLATION_CODES.contains(classCode)) {
  70. return new DataIntegrityViolationException("(" + task + "): data integrity violated by SQL '" +
  71. sql + "'", sqlex);
  72. }
  73. }
  74. // we couldn't identify it more precisely
  75. return new UncategorizedSQLException("(" + task + "): encountered SQLException [" +
  76. sqlex.getMessage() + "]", sql, sqlex);
  77. }
  78. }