Error、Exception是Throwable的两个子类。
RuntimeException是Exception的子类。Exception可分为Checked Exception(受检异常,编译时必须catch或throw)和Unchecked Exception(非受检异常,编译时可以不catch或throw)。RuntimeException及其子类是Unchecked Exception,其他Exception是Checked Exception。
Error是JVM中发生的严重错误。Error是Unchecked。
Error举例:
ThreadDeath
VirtualMachineError
OutOfMemoryError
AnnotationFormatError
ServiceConfigurationError
IOError
LinkageError
NoClassDefFoundError
ClassFormatError
AssertionError
Unchecked Exception(JDK1.8中有RuntimeException子类84个)举例:
RuntimeException
ClassCastException
IllegalArgumentException
IllegalStateException
NullPointerException
UnsupportedOperationException
FileSystemAlreadyExistsException
FileSystemNotFoundException
DateTimeException
UncheckedIOException
Checked Exception举例:
IOException
SQLException
ClassNotFoundException
Spring中Error和Unchecked Exception被事务管理器捕获会导致事务回滚。如果事务方法内被catch,没有throw不会回滚,除非手动回滚:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 。SQLException不是Unchecked Exception,不会导致事务回滚。Spring中SQLException被置换成DataAccessException,可导致事务回滚。
SQLErrorCodeSQLExceptionTranslator:
@Nullable protected DataAccessException doTranslate(String task, @Nullable String sql, SQLException ex) { SQLException sqlEx = ex; if (ex instanceof BatchUpdateException && ex.getNextException() != null) { SQLException nestedSqlEx = ex.getNextException(); if (nestedSqlEx.getErrorCode() > 0 || nestedSqlEx.getSQLState() != null) { this.logger.debug("Using nested SQLException from the BatchUpdateException"); sqlEx = nestedSqlEx; } } DataAccessException dex = this.customTranslate(task, sql, sqlEx); if (dex != null) { return dex; } else { if (this.sqlErrorCodes != null) { SQLExceptionTranslator customTranslator = this.sqlErrorCodes.getCustomSqlExceptionTranslator(); if (customTranslator != null) { DataAccessException customDex = customTranslator.translate(task, sql, sqlEx); if (customDex != null) { return customDex; } } } String errorCode; if (this.sqlErrorCodes != null) { if (this.sqlErrorCodes.isUseSqlStateForTranslation()) { errorCode = sqlEx.getSQLState(); } else { SQLException current; for(current = sqlEx; current.getErrorCode() == 0 && current.getCause() instanceof SQLException; current = (SQLException)current.getCause()) { } errorCode = Integer.toString(current.getErrorCode()); } if (errorCode != null) { CustomSQLErrorCodesTranslation[] customTranslations = this.sqlErrorCodes.getCustomTranslations(); if (customTranslations != null) { CustomSQLErrorCodesTranslation[] var8 = customTranslations; int var9 = customTranslations.length; for(int var10 = 0; var10 < var9; ++var10) { CustomSQLErrorCodesTranslation customTranslation = var8[var10]; if (Arrays.binarySearch(customTranslation.getErrorCodes(), errorCode) >= 0 && customTranslation.getExceptionClass() != null) { DataAccessException customException = this.createCustomException(task, sql, sqlEx, customTranslation.getExceptionClass()); if (customException != null) { this.logTranslation(task, sql, sqlEx, true); return customException; } } } } if (Arrays.binarySearch(this.sqlErrorCodes.getBadSqlGrammarCodes(), errorCode) >= 0) { this.logTranslation(task, sql, sqlEx, false); return new BadSqlGrammarException(task, sql != null ? sql : "", sqlEx); } if (Arrays.binarySearch(this.sqlErrorCodes.getInvalidResultSetAccessCodes(), errorCode) >= 0) { this.logTranslation(task, sql, sqlEx, false); return new InvalidResultSetAccessException(task, sql != null ? sql : "", sqlEx); } if (Arrays.binarySearch(this.sqlErrorCodes.getDuplicateKeyCodes(), errorCode) >= 0) { this.logTranslation(task, sql, sqlEx, false); return new DuplicateKeyException(this.buildMessage(task, sql, sqlEx), sqlEx); } if (Arrays.binarySearch(this.sqlErrorCodes.getDataIntegrityViolationCodes(), errorCode) >= 0) { this.logTranslation(task, sql, sqlEx, false); return new DataIntegrityViolationException(this.buildMessage(task, sql, sqlEx), sqlEx); } if (Arrays.binarySearch(this.sqlErrorCodes.getPermissionDeniedCodes(), errorCode) >= 0) { this.logTranslation(task, sql, sqlEx, false); return new PermissionDeniedDataAccessException(this.buildMessage(task, sql, sqlEx), sqlEx); } if (Arrays.binarySearch(this.sqlErrorCodes.getDataAccessResourceFailureCodes(), errorCode) >= 0) { this.logTranslation(task, sql, sqlEx, false); return new DataAccessResourceFailureException(this.buildMessage(task, sql, sqlEx), sqlEx); } if (Arrays.binarySearch(this.sqlErrorCodes.getTransientDataAccessResourceCodes(), errorCode) >= 0) { this.logTranslation(task, sql, sqlEx, false); return new TransientDataAccessResourceException(this.buildMessage(task, sql, sqlEx), sqlEx); } if (Arrays.binarySearch(this.sqlErrorCodes.getCannotAcquireLockCodes(), errorCode) >= 0) { this.logTranslation(task, sql, sqlEx, false); return new CannotAcquireLockException(this.buildMessage(task, sql, sqlEx), sqlEx); } if (Arrays.binarySearch(this.sqlErrorCodes.getDeadlockLoserCodes(), errorCode) >= 0) { this.logTranslation(task, sql, sqlEx, false); return new DeadlockLoserDataAccessException(this.buildMessage(task, sql, sqlEx), sqlEx); } if (Arrays.binarySearch(this.sqlErrorCodes.getCannotSerializeTransactionCodes(), errorCode) >= 0) { this.logTranslation(task, sql, sqlEx, false); return new CannotSerializeTransactionException(this.buildMessage(task, sql, sqlEx), sqlEx); } } } if (this.logger.isDebugEnabled()) { if (this.sqlErrorCodes != null && this.sqlErrorCodes.isUseSqlStateForTranslation()) { errorCode = "SQL state '" + sqlEx.getSQLState() + "', error code '" + sqlEx.getErrorCode(); } else { errorCode = "Error code '" + sqlEx.getErrorCode() + "'"; } this.logger.debug("Unable to translate SQLException with " + errorCode + ", will now try the fallback translator"); } return null; } }
package org.springframework.dao; import org.springframework.core.NestedRuntimeException; import org.springframework.lang.Nullable; public abstract class DataAccessException extends NestedRuntimeException { public DataAccessException(String msg) { super(msg); } public DataAccessException(@Nullable String msg, @Nullable Throwable cause) { super(msg, cause); } }