简体   繁体   English

嵌套的PDO事务不会回滚所有查询

[英]Nested PDO Transaction not rolling all queries back

I'm trying to get my head around PDO transactions to commit a fairly complex set of MySQL queries at once. 我正在设法让我关注PDO事务,以一次提交一组相当复杂的MySQL查询。 When I run the transaction however, it will commit one query and not the other - if there is a single error in either query I expect it to roll back both queries and not make changes to either table. 但是,当我运行该事务时,它将提交一个查询,而不提交另一个查询-如果任何一个查询中都存在一个错误,我希望它回滚两个查询并且不对任何一个表进行更改。

So far: My connect.php file: 到目前为止:我的connect.php文件:

class DbConnect {
    private $conn;
    function __construct() {        
    }
    /**
     * Establishing database connection
     * @return database connection handler
     */
    function connect() {
        //Where HOST, USER, PASS etc are set
        include_once "./dbconfig.php";

        // Establish the connection
        try {
        $this->conn = new PDO("mysql:host=".HOST.";dbname=".DBNAME, USER, PASS); 
        $this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $this->conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

        return $this->conn;
        } catch (PDOException $e) {
            print "Error!: " . $e->getMessage() . "<br/>";
            die();
        }
    }
}

My file where I'm trying to pass the simultaneous SQL queries 我试图传递同步SQL查询的文件

    public function transaction ($userId, $amount){
        //Creates the PDO connection EDIT: added my DB connection
        $db = new DbConnect();
        $this->conn = $db->connect();

        $con = $this->conn;

            $con->beginTransaction();
            try{
                $sql = "INSERT INTO transactions (id_user, amount) VALUES (?, ?)";
                $trans = $con->prepare($sql);
                $trans->execute([$userId, $amount]);
                //If I purposely create an error here the query above still runs in the database e.g. remove the $amount variable
                $this->updateBalance($userId, $amount);
                $con->commit();
                return true;
            }
            catch (PDOException $e) {
                $con->rollBack();
                throw $e;                    
            }
    }

    private function updateBalance ($userId, $amount){
            $time = time();
            $sql = "UPDATE balance SET balance=balance + ? WHERE user_id = ?";
            $stmt = $this->conn->prepare($sql);
            $stmt->execute([$amount, $userId]);
            $row_count = $stmt->rowCount();
            return $row_count > 0;
        }

The above is just a small sample of a bigger more complex procedure otherwise I'd just put the balance query in the same place as the transaction, however I need to keep it in a separate function. 上面只是一个更大,更复杂的过程的小样本,否则我将把余额查询放在与事务相同的位置,但是我需要将其保留在单独的函数中。 Any ideas how I can get this into an "All or nothing" commit state? 有什么想法可以使它进入“全有或全无”提交状态吗?

Well first of all you are not checking the return status of the call to your second function 好吧,首先,您不检查第二个函数的调用返回状态

$this->updateBalance($userId, $amount);

So how will you know there is an error even if there is one? 那么,即使有错误,您怎么知道有错误呢?

If you make that called function Throw an exception rather than returning a status, it should be caught by the calling blocks catch() block causing the rollback() and not the commit() 如果使那个被调用的函数抛出异常而不是返回状态,则应由调用块catch()块捕获该异常,从而引起rollback()而不是commit()

Something like this 像这样

public function transaction ($userId, $amount){
    //Creates the PDO connection
    $con = $this->conn;

    $con->beginTransaction();
    try{
        $sql = "INSERT INTO transactions (id_user, amount) VALUES (?, ?)";
        $trans = $con->prepare($sql);
        $trans->execute([$userId, $amount]);
        // If I purposely create an error here the 
        // query above still runs in the database 
        // e.g. remove the $amount variable
        $this->updateBalance($userId, $amount);
        $con->commit();
        return true;
    }
    catch (PDOException $e) {
        $con->rollBack();
        throw $e;
        return false;
    }
}

/*
* If this function throws an exception 
* rather than returning a status, then it will
* stop execution of the try block and
* be caught by the calling blocks catch() block
*/
private function updateBalance ($userId, $amount){

    $sql = "UPDATE balance SET balance=balance + ? WHERE user_id = ?";
    $stmt = $this->conn->prepare($sql);
    $res = $stmt->execute([$amount, $userId]);
    if ( ! $res ) {
        throw new Exception('It errored');
    }

}

Alternatively you could make all PDO calls throw exceptions by setting PDO::ERRMODE_EXCEPTION just after you connect to your database. 另外,您可以在连接到数据库后通过设置PDO::ERRMODE_EXCEPTION来使所有PDO调用都引发异常。

$dbh = new PDO($dsn, $user, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

However, this may be to major a change to PDO's error processing depending on how much code you have already produced. 但是,这可能是对PDO错误处理的重大更改,具体取决于您已经生成了多少代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM