Spring注解事务诡异提交全面解析
2016-07-28 15:11
447 查看
一、问题产生背景
应用上线的时候,正常调用Tomcat的shutdown.sh脚本,事务执行一半异常提交。伪代码如下:
上面是一段伪代码,实际在tomcat重启的时候,上面update语句提交,而insert没有。
二、思路解析
1、直接将Tomcat服务kill掉能否重现问题
按之前的理解是,Tomcat重启事务中断,数据库在事务连接超时后会回滚事务。那么写一段代码试一下,使用Kill -9命令中断tomcat服务后发现数据库事务竟然回滚了。
2、分析Tomcat的shutdown.sh命令
其实shutdown.sh命令最终调用的是catalina.sh命令脚本,看sh源码如下:
其实最终是调用的Bootstrap这个类来关闭服务的,我们再来看这个类的内容。
Tomcat是将注册进来的服务循环逐个关闭,这时候在关闭的时候可能会因为前一个资源关闭而造成后一个资源抛出异常,注意这个异常有可能是Throwable,也可能是Exception,后面详细解释。
3、分析Spring注解事务源码
在Tomcat关闭的时候,抛出的异常和上面代码的异常没有匹配成功,spring异常匹配采用迭代当前异常的所有父类与目标异常匹配,匹配不到后检查当前异常是否为Error或者RuntimeException的实例,是的话也能匹配上,但是没有匹配是否为Throwable的实例
三、问题总结
通过上面的问题分析,可以把代码写成如下样式:
采用Throwable捕获方能确保Tomcat异常重启,事务能够正确回滚。
应用上线的时候,正常调用Tomcat的shutdown.sh脚本,事务执行一半异常提交。伪代码如下:
@Override @Transactional(propagation = Propagation.REQUIRED) public void insert(PaymentOrder paymentOrder) { try{ paymentOrderDao.update(paymentOrder); PaymentOrderDao.insert(paymentOrder) }catch(Exception e){ logger.error(" 操作支付订单失败 biz " + paymentOrder.getBiz() + " bizOrder " + paymentOrder.getBizOrder(), e); Throw e; } }
上面是一段伪代码,实际在tomcat重启的时候,上面update语句提交,而insert没有。
二、思路解析
1、直接将Tomcat服务kill掉能否重现问题
按之前的理解是,Tomcat重启事务中断,数据库在事务连接超时后会回滚事务。那么写一段代码试一下,使用Kill -9命令中断tomcat服务后发现数据库事务竟然回滚了。
2、分析Tomcat的shutdown.sh命令
其实shutdown.sh命令最终调用的是catalina.sh命令脚本,看sh源码如下:
exec "$_RUNJAVA" $JAVA_OPTS $CATALINA_OPTS \ -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \ -Dcatalina.base="$CATALINA_BASE" \ -Dcatalina.home="$CATALINA_HOME" \ -Djava.io.tmpdir="$CATALINA_TMPDIR" \ org.apache.catalina.startup.Bootstrap "$@" stop
其实最终是调用的Bootstrap这个类来关闭服务的,我们再来看这个类的内容。
if (server instanceof Lifecycle) { try { ((Lifecycle) server).stop(); } catch (LifecycleException e) { log.error("Catalina.stop", e); } }
Tomcat是将注册进来的服务循环逐个关闭,这时候在关闭的时候可能会因为前一个资源关闭而造成后一个资源抛出异常,注意这个异常有可能是Throwable,也可能是Exception,后面详细解释。
3、分析Spring注解事务源码
在Tomcat关闭的时候,抛出的异常和上面代码的异常没有匹配成功,spring异常匹配采用迭代当前异常的所有父类与目标异常匹配,匹配不到后检查当前异常是否为Error或者RuntimeException的实例,是的话也能匹配上,但是没有匹配是否为Throwable的实例
三、问题总结
通过上面的问题分析,可以把代码写成如下样式:
@Override @Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRED) public void insert(PaymentOrder paymentOrder) { try{ paymentOrderDao.update(paymentOrder); PaymentOrderDao.insert(paymentOrder) }catch(Throwable e){ logger.error(" 操作支付订单失败 biz " + paymentOrder.getBiz() + " bizOrder " + paymentOrder.getBizOrder(), e); Throw e; } }
采用Throwable捕获方能确保Tomcat异常重启,事务能够正确回滚。
相关文章推荐
- Eclipse背景颜色,字体大小,自定义格式等...
- Java native 方法
- 6、Java并发编程:volatile关键字解析
- java中 关于运算符注意的事项
- SimpleDateFormat使用详解
- 二叉树的实现Java
- 仓库管理系统系列——3、终结篇
- java开发必备工具介绍与下载
- 出现eclipse启动不了,出现“Java was started but returned exit code=13......”的问题,下面说下,如何解决
- 一个7.0的小错误
- 利用java反射将map值封装到对象中
- java编程思想的翻译
- tomcat使用JDNI配置信息和使用信息。用于JDBC连接池
- javaWeb_监听器
- 纯Java获得本地MAC地址
- Java动态代理的实现
- javaWeb_过滤器
- javaWeb_Servlet
- eclipse & visual studio快捷键对比一览
- Java获取日期属于当年第几周