1 16 17 package org.springframework.jdbc.support; 18 19 import java.lang.reflect.Constructor ; 20 import java.sql.SQLException ; 21 import java.util.Arrays ; 22 23 import javax.sql.DataSource ; 24 25 import org.apache.commons.logging.Log; 26 import org.apache.commons.logging.LogFactory; 27 28 import org.springframework.dao.CannotAcquireLockException; 29 import org.springframework.dao.CannotSerializeTransactionException; 30 import org.springframework.dao.DataAccessException; 31 import org.springframework.dao.DataAccessResourceFailureException; 32 import org.springframework.dao.DataIntegrityViolationException; 33 import org.springframework.dao.DeadlockLoserDataAccessException; 34 import org.springframework.dao.PermissionDeniedDataAccessException; 35 import org.springframework.jdbc.BadSqlGrammarException; 36 import org.springframework.jdbc.InvalidResultSetAccessException; 37 38 63 public class SQLErrorCodeSQLExceptionTranslator implements SQLExceptionTranslator { 64 65 private static final int MESSAGE_ONLY_CONSTRUCTOR = 1; 66 private static final int MESSAGE_THROWABLE_CONSTRUCTOR = 2; 67 private static final int MESSAGE_SQLEX_CONSTRUCTOR = 3; 68 private static final int MESSAGE_SQL_THROWABLE_CONSTRUCTOR = 4; 69 private static final int MESSAGE_SQL_SQLEX_CONSTRUCTOR = 5; 70 71 72 73 protected final Log logger = LogFactory.getLog(getClass()); 74 75 76 private SQLErrorCodes sqlErrorCodes; 77 78 79 private SQLExceptionTranslator fallbackTranslator = new SQLStateSQLExceptionTranslator(); 80 81 82 86 public SQLErrorCodeSQLExceptionTranslator() { 87 } 88 89 97 public SQLErrorCodeSQLExceptionTranslator(DataSource dataSource) { 98 setDataSource(dataSource); 99 } 100 101 109 public SQLErrorCodeSQLExceptionTranslator(String dbName) { 110 setDatabaseProductName(dbName); 111 } 112 113 118 public SQLErrorCodeSQLExceptionTranslator(SQLErrorCodes sec) { 119 this.sqlErrorCodes = sec; 120 } 121 122 123 132 public void setDataSource(DataSource dataSource) { 133 this.sqlErrorCodes = SQLErrorCodesFactory.getInstance().getErrorCodes(dataSource); 134 } 135 136 144 public void setDatabaseProductName(String dbName) { 145 this.sqlErrorCodes = SQLErrorCodesFactory.getInstance().getErrorCodes(dbName); 146 } 147 148 152 public void setSqlErrorCodes(SQLErrorCodes sec) { 153 this.sqlErrorCodes = sec; 154 } 155 156 161 public SQLErrorCodes getSqlErrorCodes() { 162 return sqlErrorCodes; 163 } 164 165 171 public void setFallbackTranslator(SQLExceptionTranslator fallback) { 172 this.fallbackTranslator = fallback; 173 } 174 175 178 public SQLExceptionTranslator getFallbackTranslator() { 179 return fallbackTranslator; 180 } 181 182 183 public DataAccessException translate(String task, String sql, SQLException sqlEx) { 184 if (task == null) { 185 task = ""; 186 } 187 if (sql == null) { 188 sql = ""; 189 } 190 191 DataAccessException dex = customTranslate(task, sql, sqlEx); 193 if (dex != null) { 194 return dex; 195 } 196 197 if (this.sqlErrorCodes != null) { 199 String errorCode = null; 200 if (this.sqlErrorCodes.isUseSqlStateForTranslation()) { 201 errorCode = sqlEx.getSQLState(); 202 } 203 else { 204 errorCode = Integer.toString(sqlEx.getErrorCode()); 205 } 206 207 if (errorCode != null) { 208 209 CustomSQLErrorCodesTranslation[] customTranslations = this.sqlErrorCodes.getCustomTranslations(); 211 if (customTranslations != null) { 212 for (int i = 0; i < customTranslations.length; i++) { 213 CustomSQLErrorCodesTranslation customTranslation = customTranslations[i]; 214 if (Arrays.binarySearch(customTranslation.getErrorCodes(), errorCode) >= 0) { 215 if (customTranslation.getExceptionClass() != null) { 216 DataAccessException customException = createCustomException( 217 task, sql, sqlEx, customTranslation.getExceptionClass()); 218 if (customException != null) { 219 logTranslation(task, sql, sqlEx, true); 220 return customException; 221 } 222 } 223 } 224 } 225 } 226 227 if (Arrays.binarySearch(this.sqlErrorCodes.getBadSqlGrammarCodes(), errorCode) >= 0) { 229 logTranslation(task, sql, sqlEx, false); 230 return new BadSqlGrammarException(task, sql, sqlEx); 231 } 232 else if (Arrays.binarySearch(this.sqlErrorCodes.getInvalidResultSetAccessCodes(), errorCode) >= 0) { 233 logTranslation(task, sql, sqlEx, false); 234 return new InvalidResultSetAccessException(task, sql, sqlEx); 235 } 236 else if (Arrays.binarySearch(this.sqlErrorCodes.getDataAccessResourceFailureCodes(), errorCode) >= 0) { 237 logTranslation(task, sql, sqlEx, false); 238 return new DataAccessResourceFailureException(buildMessage(task, sql, sqlEx), sqlEx); 239 } 240 else if (Arrays.binarySearch(this.sqlErrorCodes.getPermissionDeniedCodes(), errorCode) >= 0) { 241 logTranslation(task, sql, sqlEx, false); 242 return new PermissionDeniedDataAccessException(buildMessage(task, sql, sqlEx), sqlEx); 243 } 244 else if (Arrays.binarySearch(this.sqlErrorCodes.getDataIntegrityViolationCodes(), errorCode) >= 0) { 245 logTranslation(task, sql, sqlEx, false); 246 return new DataIntegrityViolationException(buildMessage(task, sql, sqlEx), sqlEx); 247 } 248 else if (Arrays.binarySearch(this.sqlErrorCodes.getCannotAcquireLockCodes(), errorCode) >= 0) { 249 logTranslation(task, sql, sqlEx, false); 250 return new CannotAcquireLockException(buildMessage(task, sql, sqlEx), sqlEx); 251 } 252 else if (Arrays.binarySearch(this.sqlErrorCodes.getDeadlockLoserCodes(), errorCode) >= 0) { 253 logTranslation(task, sql, sqlEx, false); 254 return new DeadlockLoserDataAccessException(buildMessage(task, sql, sqlEx), sqlEx); 255 } 256 else if (Arrays.binarySearch(this.sqlErrorCodes.getCannotSerializeTransactionCodes(), errorCode) >= 0) { 257 logTranslation(task, sql, sqlEx, false); 258 return new CannotSerializeTransactionException(buildMessage(task, sql, sqlEx), sqlEx); 259 } 260 } 261 } 262 263 if (logger.isDebugEnabled()) { 265 String codes = null; 266 if (this.sqlErrorCodes.isUseSqlStateForTranslation()) { 267 codes = "SQL state '" + sqlEx.getSQLState() + 268 "', error code '" + sqlEx.getErrorCode(); 269 } 270 else { 271 codes = "Error code '" + sqlEx.getErrorCode() + "'"; 272 } 273 logger.debug("Unable to translate SQLException with " + codes + 274 ", will now try the fallback translator"); 275 } 276 return this.fallbackTranslator.translate(task, sql, sqlEx); 277 } 278 279 287 protected String buildMessage(String task, String sql, SQLException sqlEx) { 288 return task + "; SQL [" + sql + "]; " + sqlEx.getMessage(); 289 } 290 291 302 protected DataAccessException customTranslate(String task, String sql, SQLException sqlEx) { 303 return null; 304 } 305 306 319 protected DataAccessException createCustomException( 320 String task, String sql, SQLException sqlEx, Class exceptionClass) { 321 322 try { 324 int constructorType = 0; 325 Constructor [] constructors = exceptionClass.getConstructors(); 326 for (int i = 0; i < constructors.length; i++) { 327 Class [] parameterTypes = constructors[i].getParameterTypes(); 328 if (parameterTypes.length == 1 && parameterTypes[0].equals(String .class)) { 329 if (constructorType < MESSAGE_ONLY_CONSTRUCTOR) 330 constructorType = MESSAGE_ONLY_CONSTRUCTOR; 331 } 332 if (parameterTypes.length == 2 && parameterTypes[0].equals(String .class) && 333 parameterTypes[1].equals(Throwable .class)) { 334 if (constructorType < MESSAGE_THROWABLE_CONSTRUCTOR) 335 constructorType = MESSAGE_THROWABLE_CONSTRUCTOR; 336 } 337 if (parameterTypes.length == 2 && parameterTypes[0].equals(String .class) && 338 parameterTypes[1].equals(SQLException .class)) { 339 if (constructorType < MESSAGE_SQLEX_CONSTRUCTOR) 340 constructorType = MESSAGE_SQLEX_CONSTRUCTOR; 341 } 342 if (parameterTypes.length == 3 && parameterTypes[0].equals(String .class) && 343 parameterTypes[1].equals(String .class) && parameterTypes[2].equals(Throwable .class)) { 344 if (constructorType < MESSAGE_SQL_THROWABLE_CONSTRUCTOR) 345 constructorType = MESSAGE_SQL_THROWABLE_CONSTRUCTOR; 346 } 347 if (parameterTypes.length == 3 && parameterTypes[0].equals(String .class) && 348 parameterTypes[1].equals(String .class) && parameterTypes[2].equals(SQLException .class)) { 349 if (constructorType < MESSAGE_SQL_SQLEX_CONSTRUCTOR) 350 constructorType = MESSAGE_SQL_SQLEX_CONSTRUCTOR; 351 } 352 } 353 354 Constructor exceptionConstructor = null; 356 switch (constructorType) { 357 case MESSAGE_SQL_SQLEX_CONSTRUCTOR: 358 Class [] messageAndSqlAndSqlExArgsClass = new Class [] {String .class, String .class, SQLException .class}; 359 Object [] messageAndSqlAndSqlExArgs = new Object [] {task, sql, sqlEx}; 360 exceptionConstructor = exceptionClass.getConstructor(messageAndSqlAndSqlExArgsClass); 361 return (DataAccessException) exceptionConstructor.newInstance(messageAndSqlAndSqlExArgs); 362 case MESSAGE_SQL_THROWABLE_CONSTRUCTOR: 363 Class [] messageAndSqlAndThrowableArgsClass = new Class [] {String .class, String .class, Throwable .class}; 364 Object [] messageAndSqlAndThrowableArgs = new Object [] {task, sql, sqlEx}; 365 exceptionConstructor = exceptionClass.getConstructor(messageAndSqlAndThrowableArgsClass); 366 return (DataAccessException) exceptionConstructor.newInstance(messageAndSqlAndThrowableArgs); 367 case MESSAGE_SQLEX_CONSTRUCTOR: 368 Class [] messageAndSqlExArgsClass = new Class [] {String .class, SQLException .class}; 369 Object [] messageAndSqlExArgs = new Object [] {task + ": " + sqlEx.getMessage(), sqlEx}; 370 exceptionConstructor = exceptionClass.getConstructor(messageAndSqlExArgsClass); 371 return (DataAccessException) exceptionConstructor.newInstance(messageAndSqlExArgs); 372 case MESSAGE_THROWABLE_CONSTRUCTOR: 373 Class [] messageAndThrowableArgsClass = new Class [] {String .class, Throwable .class}; 374 Object [] messageAndThrowableArgs = new Object [] {task + ": " + sqlEx.getMessage(), sqlEx}; 375 exceptionConstructor = exceptionClass.getConstructor(messageAndThrowableArgsClass); 376 return (DataAccessException)exceptionConstructor.newInstance(messageAndThrowableArgs); 377 case MESSAGE_ONLY_CONSTRUCTOR: 378 Class [] messageOnlyArgsClass = new Class [] {String .class}; 379 Object [] messageOnlyArgs = new Object [] {task + ": " + sqlEx.getMessage()}; 380 exceptionConstructor = exceptionClass.getConstructor(messageOnlyArgsClass); 381 return (DataAccessException) exceptionConstructor.newInstance(messageOnlyArgs); 382 default: 383 logger.warn("Unable to find appropriate constructor of custom exception class [" + 384 exceptionClass.getName() + "]"); 385 return null; 386 } 387 } 388 catch (Throwable ex) { 389 if (logger.isWarnEnabled()) { 390 logger.warn("Unable to instantiate custom exception class [" + exceptionClass.getName() + "]", ex); 391 } 392 return null; 393 } 394 } 395 396 private void logTranslation(String task, String sql, SQLException sqlEx, boolean custom) { 397 if (logger.isDebugEnabled()) { 398 String intro = custom ? "Custom translation of" : "Translating"; 399 logger.debug(intro + " SQLException with SQL state '" + sqlEx.getSQLState() + 400 "', error code '" + sqlEx.getErrorCode() + "', message [" + sqlEx.getMessage() + 401 "]; SQL was [" + sql + "] for task [" + task + "]"); 402 } 403 } 404 405 } 406 | Popular Tags |