通过JAVA调用SQLSERVER存储过程时,存储过程内部发生了类型转换异常,在类型转换异常发生之前已经将事务打开了,并且在类型转换那一行没有采用像如下错误处理:
if @@error<>0 then
goto err
end
导致了过程在发生异常的时候直接中断返回JAVA,此时过程内部的事务并没有提交,根据MICROSOFT的文档如果存储过程内部有事务并且事务没有提交,那么JAVA的事务也不会提交,JAVA的事务须在过程事务提交以后才会被提交。当前系统也采用了连接池,此时JAVA容器直接将没有提交事务的连接再次放回连接池中,这样就影响到了后续的交易,如果后续交易使用到了该连接就会报“无法提交事务”的异常,以下是我在博客园(http://kb.cnblogs.com/b/237188/)看到的一篇文章,对这样的错误讲得非常清楚:
有个客户跟我说,他们把weblogic从923升级到923以后,总是提示failed to resume transaction的。当时没有看到任何信息,以为跟JTA超时有关,让客户把JTA timeout加大,同时把下面的设置加入jdbc-config.xml中。客户反馈过来说:还是不行。 :(
<keep-xa-conn-till-tx-complete>true</keep-xa-conn-till-tx-complete>
<rollback-local-tx-upon-conn-close>true</rollback-local-tx-upon-conn-close>
比较郁闷,后来想想,客户的jdbc是使用non-XA的driver, 同时将emulate-2pc设为了true,上面连个参数是针对XA的,测试没有效果应该是正常的。:) 客户把具体错误信息给我发了过来,如下:
####<2008-10-27 上午10时35分28秒 CST> <Error> <JDBC> <SZSEWEB-YSXAPP> <appServer11> <[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'> <<WLS Kernel>> <> <> <1225074928234> <BEA-001112> <Test "SELECT 1" set up for pool "szseWebDataSource" failed with exception: "java.sql.SQLException: [BEA][SQLServer JDBC Driver][SQLServer]服务器无法继续执行该事务。说明: 3c00000047。".>
奇怪了,对于non-XA的connection, weblogic做测试的时候,应该是不会其transaction的,而且是直接拿底层的物理connection来做个select,怎么会有transaction resume呢? 是不是底层物理连接出现了问题? 跟客户沟通了一下,大概了解了他们的应用:通过jdbc调用SQL Server的stored procedure,而stored procedure中会起自己的transaction。 Tx中的操作分两种类型:
1:业务简单,数据量大
2:数据不大,但业务比较复杂
总之,TX中的SQL执行时间比较长。
开始怀疑跟客户的stored procedure有关,建议他先去掉里面的TX,果然奏效。因为transaction一直是我比较模糊的董东,也不敢跟客户说:你不能这么写,这么写是不可以的。毕竟自己底气不足啊。不是很清楚jdbc connection的auto commit提交的到底是哪个事务,是driver的tx,还是stored procedure里的tx。应该是前者吧。花了一上午,自己搭了个测试环境,终于复现了这个问题:
存储过程:
2 as
3 begin transaction
4 waitfor delay ' 00:02:00 '
5 insert into dbo.TestT_1 values( ' test ' )
6 commit
JDBC 代码:
2
3 import com.bea.cs.test.utils.JNDIRetriver;
4 import java.sql. * ;
5 import java.io. * ;