MySQL事务难题

时间:2010-12-28 16:38:18

标签: mysql exception transactions handler

我需要在单个原子事务中执行几个插入。例如:

  

启动交易;

     

插入...

     

插入...

     

提交;

但是,当MySQL遇到错误时,它只会中止导致错误的特定语句。例如,如果第二个insert语句中存在错误,则仍将发生提交,并将记录第一个insert语句。因此,当发生错误时,MySQL事务实际上不是事务。为了解决这个问题,我使用了一个错误退出处理程序来回滚事务。现在该事务被静默中止,但我不知道是什么问题。

所以这是你的难题:

如何让MySQL在遇到错误时中止事务,并将错误代码传递给调用者?

2 个答案:

答案 0 :(得分:3)

  

我如何让MySQL在遇到错误时中止事务,并将错误代码传递给调用者?

MySQL确实将错误代码传递给调用者,并且根据此错误代码,调用者可以自由决定是否要提交当前完成的工作(忽略此特定INSERT的错误声明)或回滚交易。

这与PostgreSQL不同,ROLLBACK总是在出错时中止事务,这种行为是许多问题的根源。

<强>更新

在存储过程中使用无条件ROLLBACK是一种不好的做法。

存储过程是可堆叠的,事务不是,因此嵌套存储过程中的SAVEPOINT将回滚到事务的最开始,而不是存储过程执行的状态。

如果要使用事务来恢复错误时的数据库状态,请使用DECLARE HANDLER构造和CREATE PROCEDURE prc_work() BEGIN DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK TO sp_prc_work; SAVEPOINT sp_prc_work; INSERT …; INSERT …; … END; 回滚到保存点:

{{1}}

任一插入失败都将回滚该过程所做的所有更改并退出。

答案 1 :(得分:2)

使用Quassnoi先生的例子,这是我捕捉特定错误的最佳方法:

这个想法是使用tvariable来捕获一个简单的错误消息,然后你可以捕获你认为可能发生的自定义消息保存到变量的sql状态:

DELIMITER $$

DROP PROCEDURE IF EXISTS prc_work $$
CREATE PROCEDURE prc_work ()
BEGIN
  DECLARE EXIT HANDLER FOR SQLSTATE '23000'
  BEGIN
    SET @prc_work_error = 'Repeated key';
    ROLLBACK TO sp_prc_work;
  END;
  DECLARE EXIT HANDLER FOR SQLEXCEPTION
  BEGIN
    SET @prc_work_error = 'Unknown error';
    ROLLBACK TO sp_prc_work;
  END;

  START TRANSACTION;
    SAVEPOINT sp_prc_work;
    INSERT into test (id, name) VALUES (1, 'SomeText');
  COMMIT;
END $$

DELIMITER ;

然后你只需要进行常规调用,并对变量执行select语句,如:

call prc_work(); select @prc_work_error;

如果没有错误,则返回NULL,如果出错,则返回消息错误。如果您需要持久性错误消息,您可以选择创建一个表来存储它。

这很乏味且不够灵活,因为要为每个要捕获的状态代码需要一个DECLARE EXIT HANDLER段,它也不会显示详细的错误消息,但是嘿,它可以工作。