【学习笔记】DataAccessException

介绍

官方解释: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);
	}

}
分析
  1. Jpa 对外透出的异常比较清晰,一个自定义的顶级异常 HibernateException,一个 Spring 的 PersistenceException
  2. Mybatis 的异常处理些许复杂,需要注意的是它的 PersistenceException 是自定义的,继承于 IbatisException 异常(可以理解为Mybatis的顶级异常,但是被废弃了)
  3. SQLException 也是一个顶级异常,它是来自 JDBC 的异常,也就是我们用的 Mysql 和 Oracle 都会按照协议抛出此类异常,由 SQLExceptionTranslator 封装
  4. JedisConnectionFactory 和 LettuceConnectionFactory 都实现了 RedisConnectionFactory,而 RedisConnectionFactory 继承了 PersistenceExceptionTranslator
  5. 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");
    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值