为了表述方便,以下表结构是随便取的字段:
一,
数据库有一订单表:trade_order(id,card_no,money,trade_date,pay_date,pay_status,order_status);
数据库有一订单日志表:trade_order_log(id,card_no,log_date,message);
当前表trade_order有一条记录:
id,card_no,money,trade_date,pay_date,pay_status,order_status
1 ,null   ,null ,20101010  ,null    ,1         ,1
这条记录意思是:一客户在20101010下了一个订单,订单支付状态(pay_status=1)为待支付,订单状态(order_status=1)为有效。二,
现在客户要输入卡号、密码等信息去支付该笔订单;三,
交易程序如下:
......
Connection conn = 从连接池取连接;
conn.setAutoCommit(false);
try{
   //修改订单表信息
   try {
         String strSql = "update trade_order set card_no='2222',money=50 where id=1"
stmt = conn.createStatement();
return stmt.executeUpdate(strSql);
   } finally {
stmt.close();
   }
   //执行存储过程(存储过程功能:扣卡余额和相应日志记录)
   CallableStatement cstmt = null;
   ResultSet rs = null;
   try {
cstmt = conn.prepareCall(" {? = Call Trade(?)} "); //存储过程内容在下面
cstmt.registerOutParameter(1,java.sql.Types.BIGINT);
cstmt.setLong(2, 1);//传入订单ID:1
cstmt.executeUpdate();
   } finally {
rs.close();
cstmt.close();
   }
   //向订单日志表插入数据
   try {
         String strSql="insert into trade_order_log(id,card_no,log_date,message) values(1,‘2222’,getdate(),'成功扣了50元')";
stmt = conn.createStatement();
stmt.executeUpdate(strSql);
   } finally {
stmt.close();
   }
   //事务提交
   conn.commit();
   //日前成功扣了钱,下面要做手机充值交易
   conn.setAutoCommit(true);
   //调用手机充值模块
   //......
}catch(SQLException sqlEx){ try
{
      conn.rollback();
}
catch(SQLException sqlEx1)
{}
throw sqlEx;

}finally{
     conn.setAutoCommit(true);
}//存储过程结构如下 
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go
/*****经典交易存储过程,一般交易都调这里****/
ALTER PROC [dbo].[Trade]
 @bintOrderID bigint
as
    //通过订单ID查出卡号,和订单金额
    select * from trade_order where id=@bintOrderID
    update trade_order set pay_status=2,pay_date=getdate() where id=@bintOrderID
    //通过卡号查出卡帐户ID,然后更新卡帐户余额
    /*更新卡帐户余额*/
    update account_info set account_balance=account_balance-订单金额 where account_id=卡帐户ID四、程序就这么多,问题是有时(下面这种情况发生很少)
1,存储过程好像没执行(卡余额没扣除)但订单日志表:trade_order_log里却成功插入了一条日志,下面的手机充值模块执行了
2,存储过程执行了(卡余额扣了),但订单表trade_order里的卡号等信息没update掉,订单日志表:trade_order_log里却成功插入了一条日志,下面的手机充值模块执行了大家帮忙分析下   

解决方案 »

  1.   

    补充:
    由于JAVA程序和存储过程在同一事务里操作trade_order的同一条记录,不知道是不是有问题
      

  2.   

    你确定外面的catch能捕获到你内部三个try块的异常?
    你确定抛出的是SQLException ?而不是别的什么类型的Excepiton
      

  3.   

    它要是抛异常,下面的交易日志表(trade_order_log)就不会有数据插入成功了
      

  4.   

    try {
      String strSql = "update trade_order set card_no='2222',money=50 where id=1"
    stmt = conn.createStatement();
    return stmt.executeUpdate(strSql);
      } finally {
    stmt.close();
      }
    这里是一个问题。
    还有,你的代码这样写很不规范。去看看如何执行多个SQL语句吧。
    另外finally不是万能的。如果要重复使用stmt的话,去看下JDK的描述
      

  5.   

    wang_shx 可不可以详细点呢:
    return stmt.executeUpdate(strSql); 
    有什么问题?
      

  6.   

    笔误:
    return stmt.executeUpdate(strSql); 这个地方没方return的,这个笔误。原项目中,这3个数据库操作,都是方法调用的,所以有return 
    我为了描述方便 ,就把3个方法内容copy到一起,不小心多了个return的
      

  7.   

    又出问题了,是存储过程好像没执行又没报错,因为只有存储过程里SQL语句操作的表没变化,其它的都成功了
      

  8.   

    感觉是你 coon的事务处理上有问题 存储过程和单独的SQL语句把 事务放到正常里面提交 不行的话 把存储过程那一块也写到程序中 作为模块吧
      

  9.   

    项目中的存储过程中是有大量SQL语句的,放到程序中话,会影响性能
    我有一个方案:就是让存储过程执行完返回一个值,java程序判断是否有返回值,来判断是否做rollback,只是不知道为什么会出现问题,没发做测试,测试结果又都是正常的
      

  10.   

    建议你单独 分别调试下:1.就是 在SqlServer数据库查询分析器中按照你编写的存储过程保存编译后,去放入你的测试参数调试下你的存储过程,看看存储过程是否达到了你的预期效果,如果单独在数据库里都没达到效果,说明错误出在数据里的存储过程,如果达到效果了,2.再把java 里写的jdbc程序中其他那两个数据库操作:就是开始的update和最后放入日志的insert在java里测试,如果这两部分也没有错误在数据里有了记录并更改了记录说明你写的jdbc没有错误,3.如果以上两步都没有错误就按你贴出来的情形,把update 、调用存储过程、 insert 再在java里测试一把,如果有错误就考虑下在jdbc写法上或者jdbc在同一个 statement对象调用问题
      

  11.   

    这是在线支付系统里的程序,每天有几百条订单生成,都正常的,只是偶尔出现这种情况,就是订单支付成功了,没扣款,我先在存储过程返回值java里判断下,要是还不行,就不用存储过程了,
      

  12.   

    你查看下procedure中有没有提交的语句,如果有的话,外部的会失效的。
    如果可以的话,最好debug下你的rollback有没有生效
      

  13.   

    项目中的存储过程是这样的:
    USE [rtcard]
    GO
    /****** Object:  StoredProcedure [dbo].[prcd_DoTrade]    Script Date: 10/13/2010 15:28:34 ******/
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO/*****经典交易存储过程,一般交易都调这里****/
    ALTER PROC [dbo].[prcd_DoTrade]
     @bintOrderID bigint,
     @bintAdminID bigint,
     @varIPAddress varchar(15)
    as
        --支付帐户ID
        DECLARE @bintPayAccountID bigint
        --支付金额
        DECLARE @bintPayMoney bigint
        --接收帐户ID
        DECLARE @bintRcvAccountID bigint
        --接收金额
        DECLARE @bintRcvMoney bigint
        --支付帐户余额
        DECLARE @bintPayAccBalance bigint
        --接收帐户余额
        DECLARE @bintRcvAccBalance bigint    /*取出订单中的帐户ID和支付接收金额*/
        select @bintPayAccountID=pay_account_id,@bintPayMoney=pay_money,@bintRcvAccountID=rcv_account_id,@bintRcvMoney=rcv_money from trade_order where order_id=@bintOrderID    /*查询取出帐户变动前的帐户余额*/
        select @bintPayAccBalance=account_balance from account_info where account_id=@bintPayAccountID
        select @bintRcvAccBalance=account_balance from account_info where account_id=@bintRcvAccountID    /*插入交易日志(记录帐户变动前的帐户余额状态)*/
        if @bintAdminID is null or @bintAdminID=0 
          insert into trade_log(order_id,pay_account_id,pay_account_balance,rcv_account_id,rcv_account_balance,ip_address) values(@bintOrderID, @bintPayAccountID, @bintPayAccBalance, @bintRcvAccountID, @bintRcvAccBalance, @varIPAddress)
        else
          insert into trade_log(order_id,pay_account_id,pay_account_balance,rcv_account_id,rcv_account_balance,employee_id,ip_address) values(@bintOrderID, @bintPayAccountID, @bintPayAccBalance, @bintRcvAccountID, @bintRcvAccBalance, @bintAdminID, @varIPAddress)    /*更新支付订单记录支付状态为支付成功*/
        update trade_order set pay_date=getdate(),pay_status=3 where order_id=@bintOrderID    /*更新帐户余额*/
        update account_info set account_balance=account_balance-@bintPayMoney where account_id=@bintPayAccountID
        update account_info set account_balance=account_balance+@bintRcvMoney where account_id=@bintRcvAccountID
      

  14.   

    建议存储过程中加入事务,最后返回直接结果,可定义一个状态码表示。
    java程序中的事务,根据存储过程的执行结果判断整个事务事务是否提交。毕竟账户金额操作最重要。
      

  15.   

    不知道SqlServer会怎么处理存储过程和嵌套事务但是Oracle是只要你调用了存储过程,就会启动一个事务并且该事务在存储过程结束后自动commit所以肯定会出异常。
      

  16.   

    我测试了sqlserver存储过程,存储过程只要抛异常,java里会rollback成功的,也就是说我上面写的逻辑是对的,但为什么“有时”存储过程像没执行呢
      

  17.   

    ^*)^*&^*&^%%%^*&KJHJKDSF##$
      

  18.   

    你查查 sqlserver文档 是不是 在sqlserver里的存储过程中有类似的自动提交 回滚的问题?或者 在java中加载 sqlserver驱动时有:这个错误产生的原因一般是当你在一个SQL SERVER的JDBC连接上执行多个STATEMENTS的操作,或者是手动事务状态(AutoCommit=false) 并且在使用sqlserver驱动的url时的 direct (SelectMethod=direct) 模式. Direct 模式是默认的模式. 
    解决办法
    当你使用手动事务模式时,必须把SelectMethod 属性的值设置为 Cursor, 或者是确保在你的连接上只有一个STATEMENT操作。
    以前遇到过SQL Server有很多问题,否则可能在访问期间由于在一个Connection对象中使用多个不同Statement(preparedStatement或callalbeStatement)对象的现象,造成在java中出现克隆的Connection,引起的事务问题吧,你看看把驱动url上多加上一个 SelectMethod=cursor :即jdbc:microsoft:sqlserver://localhost:port;DatabaseName=xxx;SelectMethod=cursor 试试看