﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-justuszhang2009-随笔分类-mybatis</title><link>http://www.blogjava.net/justuszhang2009/category/48889.html</link><description /><language>zh-cn</language><lastBuildDate>Mon, 20 Jun 2011 23:36:06 GMT</lastBuildDate><pubDate>Mon, 20 Jun 2011 23:36:06 GMT</pubDate><ttl>60</ttl><item><title>mybatis批量更新的问题</title><link>http://www.blogjava.net/justuszhang2009/archive/2011/06/21/352698.html</link><dc:creator>张益刚</dc:creator><author>张益刚</author><pubDate>Mon, 20 Jun 2011 23:19:00 GMT</pubDate><guid>http://www.blogjava.net/justuszhang2009/archive/2011/06/21/352698.html</guid><wfw:comment>http://www.blogjava.net/justuszhang2009/comments/352698.html</wfw:comment><comments>http://www.blogjava.net/justuszhang2009/archive/2011/06/21/352698.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/justuszhang2009/comments/commentRss/352698.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/justuszhang2009/services/trackbacks/352698.html</trackback:ping><description><![CDATA[<p>一、问题描述<br />场景描述：有这样一个service方法，调用了两个dao中的方法。第一个方法按照传入的id批量更新用户名。第二个dao方法无数据库操作，仅仅抛出一个RuntimeException. <br />这个service方法通过xml配置由spring事务管理的。<br />两个DAO类中分别有SqlSessionTemplate类型的属性template，使用IOC的方式注入的。<br />&nbsp;public void batchUpdate() {<br />&nbsp;&nbsp;String username="newname59";</p>
<p>&nbsp;&nbsp;List&lt;Integer&gt; idList=Arrays.asList(10000,10001);<br />&nbsp;&nbsp;userDao.batchUpdateUsername(username, idList);<br />&nbsp;&nbsp;testDao.testException();<br />&nbsp;&nbsp;userDao.batchUpdateUserage(55, idList);<br />&nbsp;&nbsp;testDao.testNormal();&nbsp;<br />&nbsp;}<br />当userDao及testDao中注入的是ExcutorType.Simple类型的template时，批量更新用户名的操作会回滚。<br />当userDao及testDao中注入的是ExcutorType.Batch类型的template时，批量更新用户名的操作未回滚。</p>
<p>经过检查数据库日志，发现第二种情况的数据库执行序列如下：<br />1 set autocommit = 0<br />2 rollback<br />3 update t_user set username="newname59" where id = '10000'<br />4 update t_user set username="newname59" where id = '10001'<br />5 set autocommit = 1<br />更新操作在回滚之后执行，故回滚失败。</p>
<p>调试源代码发现有如下序列：<br />AbstractPlatformTransactionManager <br />&nbsp;processRollback （） --&gt; triggerAfterCompletion() --&gt; invokeAfterCompletion()<br />--&gt; <br />TransactionSynchronizationUtils <br />&nbsp;invokeAfterCompletion() <br />--&gt; <br />SqlSessionUtils<br />&nbsp;&nbsp;&nbsp; afterCompletion()<br />--&gt;<br />DefaultSqlSession<br />&nbsp;close()<br />--&gt;<br />BaseExecutor<br />&nbsp;close() --&gt; rollback() --&gt; flushStatement()<br />--&gt;<br />BatchExecutor<br />&nbsp;doFlushStatements()<br />这时就执行了sql语句。<br />&nbsp;<br />简单来说：抛出异常，spring事务回滚，清理资源关闭sqlSession.<br />mybatis关闭sqlsession,关闭前先flushStatements，执行未执行的sql语句，然后再rollback.<br />但是这个rollback方法里判断connection是受事务管理的，就不执行任何操作。<br />&nbsp;<br />&nbsp;public void rollback(boolean required) throws SQLException {<br />&nbsp;&nbsp;&nbsp; if (!closed) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; clearLocalCache();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; flushStatements();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } finally {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (required) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; transaction.rollback();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp; }&nbsp;</p>
<p>&nbsp;&nbsp; public void rollback() throws SQLException {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!this.isConnectionTransactional) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (this.logger.isDebugEnabled()) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.logger.debug("Rolling back JDBC Connection [" + this.connection + "]");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.connection.rollback();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;<br />二、解决办法：<br />&nbsp;1、在自己的应用程序中写个拦截器。在执行完executor的close()之后，由这个拦截器再执行一遍connection.rollback()，但从代码的可读性来看，会非常的差。<br />&nbsp;2、修改mybatis的bug。修改BaseExecutor的rollback()<br />&nbsp;public void rollback(boolean required) throws SQLException {<br />&nbsp;&nbsp;&nbsp; if (!closed) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; clearLocalCache();<br />&nbsp;&nbsp;if (!required) {&nbsp;<br />&nbsp;&nbsp;&nbsp;flushStatements();<br />&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } finally {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (required) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; transaction.rollback();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp; }&nbsp;<br /><br />不知道大家有没有碰到过类似的问题，又是通过什么方案解决的呢？</p> <img src ="http://www.blogjava.net/justuszhang2009/aggbug/352698.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/justuszhang2009/" target="_blank">张益刚</a> 2011-06-21 07:19 <a href="http://www.blogjava.net/justuszhang2009/archive/2011/06/21/352698.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>