介绍
官方解释:Root of the hierarchy of data access exceptions。可以理解为它是处理数据层面的顶级异常
往上看:DataAccessException 继承了 NestedRuntimeException,而 NestedRuntimeException 继承了 RuntimeException。所以它是个运行时异常
往下看:DataAccessException 类是其他异常封装出来的,具体的封装接口是 PersistenceExceptionTranslator,封装的异常类型取决于你项目中引入的数据层面的中间件,常见的有 Mybatis,Hibernate,Jedis,Lettuce等
具体封装
public class HibernateExceptionTranslator implements PersistenceExceptionTranslator {
@Nullable
private SQLExceptionTranslator jdbcExceptionTranslator;
public HibernateExceptionTranslator() {
}
public void setJdbcExceptionTranslator(SQLExceptionTranslator jdbcExceptionTranslator) {
this.jdbcExceptionTranslator = jdbcExceptionTranslator;
}
@Nullable
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
// The base exception type for Hibernate exceptions
if (ex instanceof HibernateException) {
return this.convertHibernateAccessException((HibernateException)ex);
} else if (ex instanceof PersistenceException) {
return ex.getCause() instanceof HibernateException ? this.convertHibernateAccessException((HibernateException)ex.getCause()) : EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(ex);
} else {
return null;
}
}
protected DataAccessException convertHibernateAccessException(HibernateException ex) {
if (this.jdbcExceptionTranslator != null && ex instanceof JDBCException) {
JDBCException jdbcEx = (JDBCException)ex;
DataAccessException dae = this.jdbcExceptionTranslator.translate("Hibernate operation: " + jdbcEx.getMessage(), jdbcEx.getSQL(), jdbcEx.getSQLException());
if (dae != null) {
throw dae;
}
}
return SessionFactoryUtils.convertHibernateAccessException(ex);
}
}
public class MyBatisExceptionTranslator implements PersistenceExceptionTranslator {
private final Supplier<SQLExceptionTranslator> exceptionTranslatorSupplier;
private SQLExceptionTranslator exceptionTranslator;
public MyBatisExceptionTranslator(DataSource dataSource, boolean exceptionTranslatorLazyInit) {
this(() -> {
return new SQLErrorCodeSQLExceptionTranslator(dataSource);
}, exceptionTranslatorLazyInit);
}
public MyBatisExceptionTranslator(Supplier<SQLExceptionTranslator> exceptionTranslatorSupplier, boolean exceptionTranslatorLazyInit) {
this.exceptionTranslatorSupplier = exceptionTranslatorSupplier;
if (!exceptionTranslatorLazyInit) {
this.initExceptionTranslator();
}
}
public DataAccessException translateExceptionIfPossible(RuntimeException e) {
if (e instanceof PersistenceException) {
if (((RuntimeException)e).getCause() instanceof PersistenceException) {
e = (PersistenceException)((RuntimeException)e).getCause();
}
if (((RuntimeException)e).getCause() instanceof SQLException) {
this.initExceptionTranslator();
return this.exceptionTranslator.translate(((RuntimeException)e).getMessage() + "\n", (String)null, (SQLException)((RuntimeException)e).getCause());
} else if (((RuntimeException)e).getCause() instanceof TransactionException) {
throw (TransactionException)((RuntimeException)e).getCause();
} else {
return new MyBatisSystemException((Throwable)e);
}
} else {
return null;
}
}
private synchronized void initExceptionTranslator() {
if (this.exceptionTranslator == null) {
this.exceptionTranslator = (SQLExceptionTranslator)this.exceptionTranslatorSupplier.get();
}
}
}
public class LettuceConnectionFactory
implements InitializingBean, DisposableBean, RedisConnectionFactory, ReactiveRedisConnectionFactory {
private static final ExceptionTranslationStrategy EXCEPTION_TRANSLATION = new PassThroughExceptionTranslationStrategy(
LettuceConverters.exceptionConverter());
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
return EXCEPTION_TRANSLATION.translate(ex);
}
}
public class JedisConnectionFactory
implements InitializingBean, DisposableBean, RedisConnectionFactory {
private static final ExceptionTranslationStrategy EXCEPTION_TRANSLATION = new PassThroughExceptionTranslationStrategy(
JedisConverters.exceptionConverter());
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
return EXCEPTION_TRANSLATION.translate(ex);
}
}
分析
- Jpa 对外透出的异常比较清晰,一个自定义的顶级异常 HibernateException,一个 Spring 的 PersistenceException
- Mybatis 的异常处理些许复杂,需要注意的是它的 PersistenceException 是自定义的,继承于 IbatisException 异常(可以理解为Mybatis的顶级异常,但是被废弃了)
- SQLException 也是一个顶级异常,它是来自 JDBC 的异常,也就是我们用的 Mysql 和 Oracle 都会按照协议抛出此类异常,由 SQLExceptionTranslator 封装
- JedisConnectionFactory 和 LettuceConnectionFactory 都实现了 RedisConnectionFactory,而 RedisConnectionFactory 继承了 PersistenceExceptionTranslator
- Redis 两种客户端(jedis和lettuce)的异常处理有意思的使用了策略设计模式,他们先构造各自的策略,然后调用 spring 的 coverter 的顶级接口去进行转换,具体的实现要去看 JedisExceptionConverter 和 LettuceConverters
总结
@ExceptionHandler({DataAccessException.class})
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public ApiResult<Void> handleSQLException(Exception exception) {
return ApiResult.fail("DB ERROR");
}