Mybatis随笔(四) 异常处理ErrorContext
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis随笔(四) 异常处理ErrorContext相关的知识,希望对你有一定的参考价值。
参考技术A Mybatis 使用 ErrorContext 来做异常的统一处理大体上就是使用一个 ThreadLocal<T> 来存储当前线程的异常信息,当异常发生时可以根据这些信息快速地定位问题,一目了然
先贴下源码
其实都很易读,精髓就是那个 ThreadLocal<ErrorContext> 类型的类变量 LOCAL .
看下其方法
一个私有的构造方法,是一个单例模式的应用,通过 ThreadLocal 实现
ThreadLocal 是一块线程私有的领地,可以随时从线程上下文中拿到这份变量的副本,是多线程下安全访问共享变量的一种解决方案。
1.4 引入,但其返回的都是Object类型对象,需要强转;在 1.5 时添加泛型后得到了极大的增强
关于 ThreadLocal 其大名鼎鼎的就是可能会造成内存泄漏,详细可参考 ThreadLocal内存泄漏真因探究
而 ErrorContext 就是一种很好的 ThreadLocal 应用实例。
resource / activity / object / message / sql / cause 这些方法都是会使用到 ErrorContext 的地方,也就是会创建 ThreadLocal
如果你跟着调用链往上追溯,都会是下面三个点
清理方法是 reset ,看下调用
与使用对应,再看下具体调用方式
其他的都是一样,通过 try-catch-finally 的方式确保 ThreadLocal 的释放
除了以上方法,我们注意到还有两个特殊方法 store 和 recall
ErrorContext 还有一个属性用于存储自身
将当前对象保存起来,同时新建一个 ErrorContext 返回
很明显,恢复之前存储起来的 ErrorContext
再来看下调用
发现是在 KeyGenerator # processBefore 方法前后调用的,很明显是怕此方法污染了异常信息,具体原因需要再深入看下这个方法。
可参考 mybatis 源码分析 KeyGenerator 详解
这里对比一下另一种写法(应该是低版本的实现,其实无所谓什么版本,只要能触发我们思考就值得讲一讲)
这种写法会有什么问题吗?
首先我们明确几点
那么上述写法就做了如下三件事
第一步 A@ErrorContext.stored = A@ErrorContext
第二步 新建 ErrorContex 对象 B@ErrorContext
第三步 将 B@ErrorContext 放入 ThreadLocal
这里请注意一下, B@ErrorContext.stored 是空的
那么下次调用 recall 方法来恢复的时候还能拿到 stored 下来的对象吗?
是拿不到的。
ThreadLocal内存泄漏真因探究
Mybatis源码研究之ErrorContext
mybatis 源码分析 KeyGenerator 详解
MyBatis异常处理org.apache.ibatis.executor.ExecutorException
由于测试缘故,数据库被加入了很多测试数据。再次执行原来的查询出现了异常org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.executor.ExecutorException: Statement returned more than one row, where no more than one was expected.
通过描述可以看到,期待返回结果数应该小于等于1,但却返回多条记录。开始由于查询返回的对象是A,检查了一下A发现没有重复记录,随后发现A类内部有成员B。
class A
B b;
而在Mapper中的定义的Sql中查询b是返回唯一的结果,而不是一个List。因此是在查询b的时候造出现了异常。最后清理掉数据,异常解决。 所以当遇到这种异常时候不但要关注查询返回结果时候唯一,还要注意所查询的结果中包含的成员对象是否允许有多个。
以上是关于Mybatis随笔(四) 异常处理ErrorContext的主要内容,如果未能解决你的问题,请参考以下文章
SpringBoot系列优雅的处理统一异常处理与统一结果返回
MyBatis异常处理org.apache.ibatis.executor.ExecutorException