spring 事务对存储过程管用吗?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring 事务对存储过程管用吗?相关的知识,希望对你有一定的参考价值。

spring 只能对java层进行控制.
如果需要对存储过程也添加事物,那么必须自己编写存储过程,需要对存储过程十分熟练.
在每个存储过程中添加一个入参,这个入参就是控制事物的.
我见过一家期货公司就是这么用存储过程的.
参考技术A 不管用追问

怎么实现Spring事务控制提交?

参考技术B 在你的存储过程中不要加入事务控制语句,事务控制全部交给spring来做

在包含动态 sql 的事务中嵌入对存储过程的调用

【中文标题】在包含动态 sql 的事务中嵌入对存储过程的调用【英文标题】:embedding a call to a stored procedure with in a transaction that contains dynamic sql 【发布时间】:2014-01-13 15:46:51 【问题描述】:

我想在插入选择语句的末尾调用一个存储过程 但在事务的提交或回滚之前,我希望存储过程成为包含插入选择的事务的一部分。如果我希望新代码成为包含事务的一部分,那么编写动态 SQL 是唯一的选择吗?

字符串追加的数量实际上是相当大的(数千个),但我只分享了一小段代码,以突出概念并展示我想要做什么。

sqlStr.Append("BEGIN TRANSACTION; ")

sqlStr.Append("INSERT INTO table_chg (Column1, Column2) ")                                             
sqlStr.Append("SELECT r.Column8 , r.Column9 ")
sqlStr.Append("FROM v_table r, table1 r ")
sqlStr.Append("WHERE r.columnName1 = '1' ")
sqlStr.Append("  AND r.columnName2 = '2'; ")
sqlStr.Append(" ") 

/** ADD STORED PROCEDURE HERE    */

EXEC sp_storedProc '1'   **THIS DOES NOT WORK. BUT 1 does resolve to the correct variable that I need. Also the stored procedure works standalone and the VB.Net code does try to reference the stored procedure because if I give the stored procedure an incorrect name, I get an error back which says "stored procedure not found**

sqlStr.Append("IF @@ERROR <> 0 ")
sqlStr.Append("    ROLLBACK TRANSACTION ")
sqlStr.Append("ELSE ")
sqlStr.Append("    COMMIT TRANSACTION; ")

strError = DatabaseClass.ExecuteNonQueryReturnError (String.Format(sqlStr.ToString(), parameter1, parameter2))

【问题讨论】:

关于可行性的任何想法? 我认为这是您为生成示例所做的简化的副产品,但您对“v_table”和“table1”都使用了别名“r”。这将导致错误。 是的,没错。它实际上是一个巨大的 sql 语句,我减少了.....很好的观察...... 【参考方案1】:

    @@ERROR 函数在发生错误后一旦执行任何其他语句就会重置为 null,因此最好在您认为可能发生错误的地方使用变量。

    在您的连接条件中,您为两个表使用了相同的别名r,这将导致错误。别名应该不同。

    在您的联接中使用ON 子句

对于您的@@ERROR 函数语句应该类似于

@@ERROR

BEGIN TRANSACTION
 DECLARE @Error INT;

    INSERT INTO table_chg (Column1, Column2)                                             
    SELECT r.Column8 , r.Column9 
    FROM v_table r1 INNER JOIN table1 r2          --<-- Alias should be different for 
    ON r1.ReferencingColumn = r2.ReferencingColumn     -- for both tables
    WHERE r1.columnName1  = 1         --<-- Use r1 or r2 alias here
     AND  r2.columnName2 = 2

  SET   @Error = @@ERROR;

IF (@Error <> 0) 
  ROLLBACK TRANSACTION 
ELSE 
  COMMIT TRANSACTION

Try..Catch 块

我建议你使用try catch 块,类似这样,它允许你在catch 块中使用错误函数并访问有关错误的详细信息。

BEGIN TRY

  BEGIN TRANSACTION
     DECLARE @Error INT;

        INSERT INTO table_chg (Column1, Column2)                                             
        SELECT r.Column8 , r.Column9 
        FROM v_table r1 INNER JOIN table1 r2          --<-- Alias should be different for 
        ON r1.ReferencingColumn = r2.ReferencingColumn     -- for both tables
        WHERE r1.columnName1  = 1         --<-- Use r1 or r2 alias here
         AND  r2.columnName2 = 2

  COMMIT TRANSACTION  

END TRY

BEGIN CATCH
 IF (@@TRANCOUNT > 0) 
  ROLLBACK TRANSACTION 

      SELECT ERROR_NUMBER() AS ERRORNUMBER,
             ERROR_MESSAGE() AS ERRORMESSAGE
             .
             ...... (All the other error functions)
END CATCH

【讨论】:

【参考方案2】:

这是你需要做的:

1 - 创建事务对象并使用Try-block

2 - 使用您的事务对象,执行SqlCommand.ExecuteNonQuery(插入) - 它将返回受影响的记录数。

3 - 使用您的事务对象,执行 SqlCommand.ExecuteNonQuery(存储过程) - 它将返回受影响的记录数和 [如果需要] 输出参数。

4 - 根据第 2 步和第 3 步的结果回滚或提交事务对象

5 - 不要这样做

sqlStr.Append("INSERT INTO table_chg (Column1, Column2) ")                                               
sqlStr.Append("SELECT r.Column8 , r.Column9 ").....

改为这样做:

Dim sql as String = _
    "INSERT INTO table_chg (Column1, Column2) " & _
    "SELECT r.Column8 , r.Column9 ".......

【讨论】:

你能解释一下#5 的原因吗?虽然原始海报没有这么说,但看起来他或她正在使用StringBuilder。改用串联字符串有什么好处? 字符串生成器在处理循环时是绝对必须的。但是,如果事先知道字符串,最好声明一个字符串。字符串生成器占用资源。如果您不初始化它,它会在您添加数据时自行调整大小,这将占用更多资源。在目前的情况下,使用string 比较合适。字符串是字节数组,字符串生成器是管理它的机制。在这里我们不需要它。 你是对的。我们不应该使用字符串生成器。但不幸的是,我被这个代码库困住了,而这段代码所做的只是用 Sql 代码构建字符串.....【参考方案3】:

你可以尝试使用TransactionScope

Using scope As TransactionScope = New TransactionScope
Try
'Here will be the DB call code
'Make some verifications if everyting was ok in DB
SCope.Complete()

Catch ex As Exception
End Try
End Using

我不会使用 SQL 中的 BEGIN TRY END TRY,因为如果发生错误,我不会一直捕获。例如,如果您调用了一个方法 throw 一个链接服务器并且在链接服务器上发生了错误,则 begin try end try 将不会捕获该错误:此外,“A TRY...CATCH 构造捕获所有严重程度高于 10 的执行错误不关闭数据库连接。”

如果您使用 TRANSACTIONSCOPE,您可以从 SQL 中删除 TRANSACTION。如果 scope.Complete() 由于脚本执行后的错误或验证而未被调用,则所有内容都将回滚。

【讨论】:

以上是关于spring 事务对存储过程管用吗?的主要内容,如果未能解决你的问题,请参考以下文章

一个存储过程中更新多个表可以用一个COMMIT吗 ?

在包含动态 sql 的事务中嵌入对存储过程的调用

mysql存储过程中最后有commit和没有commit 是有所不同的。(为测试但是碰到过这个问题)

spring中如何调用存储过程

这样的存储过程会导致死锁吗?

应该在存储过程之外还是在内部指定事务?