我正试图绕过PDO事务,一次提交一组相当复杂的MySQL查询。但是,当我运行事务时,它将提交一个查询而不提交另一个查询 - 如果任一查询中存在单个错误,我希望它回滚两个查询而不对这两个表进行更改。
到目前为止: 我的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();
}
}
}
我正在尝试传递同步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;
}
以上只是更大更复杂程序的一小部分示例,否则我只是将余额查询放在与事务相同的位置,但是我需要将它保存在单独的函数中。任何想法我怎么能把它变成“全有或全无”提交状态?
答案 0 :(得分:0)
首先,您没有检查第二个功能的调用的返回状态
$this->updateBalance($userId, $amount);
那么,即使有错误,您怎么知道错误?
如果你使那个被调用的函数抛出异常而不是返回一个状态,它应该被调用块catch()块捕获,导致rollback()
而不是commit()
像这样的东西
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');
}
}
或者,您可以通过在连接到数据库后设置PDO::ERRMODE_EXCEPTION
来使所有PDO调用抛出异常。
$dbh = new PDO($dsn, $user, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
但是,这可能是对PDO错误处理的重大改变,具体取决于您已经生成了多少代码。