我在 LAPP 环境(linux apache postgresql php)上工作,我只是想了解如何在事务中使用准备好的语句(如果可能的话)。
我希望代码能比文字更好地解释:
示例1,简单交易:
BEGIN;
INSERT INTO requests (user_id, description, date) VALUES ('4', 'This dont worth anything', NOW());
UPDATE users SET num_requests = (num_requests + 1) WHERE id = '4';
--something gone wrong, cancel the transaction
ROLLBACK;
UPDATE users SET last_activity = NOW() WHERE id = '4'
COMMIT;
在上面的例子中,如果我没有正确理解事务,数据库中唯一的影响将是 last_activity 的更新......是吗?
如果我尝试在 php 中使用该事务(使用 PDO 或 pg_ 方法),则代码应如下所示(示例 2):
/* skip the connection */
pg_query($pgConnection, "BEGIN");
pg_query($pgConnection, "INSERT INTO requests (user_id, description, date) VALUES ('$id_user', 'This dont worth anything', NOW())");
pg_query($pgConnection, "UPDATE users SET num_requests = (num_requests + 1) WHERE id = '$id_user'");
//something gone wrong, cancel the transaction
pg_query($pgConnection, "ROLLBACK");
pg_query($pgConnection, "UPDATE users SET last_activity = NOW() WHERE id = '$id_user'");
pg_query($pgConnection, "COMMIT");
这很好用。也许难看,但似乎有效(建议总是受欢迎的)
无论如何,当我尝试将示例 2 与准备好的语句结合起来时,我的问题就来了(我知道在示例 2 中使用准备好的语句不是很有用)
示例 3:
/* skip the connection */
pg_prepare($pgConnection, 'insert_try', "INSERT INTO requests (user_id, description, date) VALUES ('$1', '$2', $3)");
pg_query($pgConnection, "BEGIN");
pg_execute($pgConnection, 'insert_try', array($user_id, 'This dont worth anything', date("Y-m-d")));
/* and so on ...*/
好吧,示例 3 根本不起作用,如果事务在回滚中到期,则准备好的语句将有效。
那么,准备好的语句不能在事务中使用,还是我走错了路?
编辑:
在尝试了 PDO 之后,我到了这一点:
<?php
$dbh = new PDO('pgsql:host=127.0.0.1;dbname=test', 'myuser', 'xxxxxx');
$rollback = false;
$dbh->beginTransaction();
//create the prepared statements
$insert_order = $dbh->prepare('INSERT INTO h_orders (id, id_customer, date, code) VALUES (?, ?, ?, ?)');
$insert_items = $dbh->prepare('INSERT INTO h_items (id, id_order, descr, price) VALUES (?, ?, ?, ?)');
$delete_order = $dbh->prepare('DELETE FROM p_orders WHERE id = ?');
//move the orders from p_orders to h_orders (history)
$qeOrders = $dbh->query("SELECT id, id_customer, date, code FROM p_orders LIMIT 1");
while($rayOrder = $qeOrders->fetch(PDO::FETCH_ASSOC)){
//h_orders already contain a row with id 293
//lets make the query fail
$insert_order->execute(array('293', $rayOrder['id_customer'], $rayOrder['date'], $rayOrder['code'])) OR var_dump($dbh->errorInfo());
//this is the real execute
//$insert_order->execute(array($rayOrder['id'], $rayOrder['id_customer'], $rayOrder['date'], $rayOrder['code'])) OR die(damnIt('insert_order'));
//for each order, i move the items too
$qeItems = $dbh->query("SELECT id, id_order, descr, price FROM p_items WHERE id_order = '" . $rayOrder['id'] . "'") OR var_dump($dbh->errorInfo());
while($rayItem = $qeItems->fetch(PDO::FETCH_ASSOC)){
$insert_items->execute(array($rayItem['id'], $rayItem['id_order'], $rayItem['descr'], $rayItem['price'])) OR var_dump($dbh->errorInfo());
}
//if everything is ok, delete the order from p_orders
$delete_order->execute(array($rayOrder['id'])) OR var_dump($dbh->errorInfo());
}
//in here i'll use a bool var to see if anythings gone wrong and i need to rollback,
//or all good and commit
$dbh->rollBack();
//$dbh->commit();
?>
上面的代码失败,输出如下:
array(3) { [0]=> string(5) "00000" [1]=> int(7) [2]=> string(62) "错误:重复键违反唯一约束 "id_h_orders"" }
array(3) { [0]=> string(5) "25P02" [1]=> int(7) [2]=> string(87) "错误:当前事务被中止,命令被忽略直到事务块结束" }
致命错误:在第 23 行的 /srv/www/test-db/test-db-pgsql-08.php 中的非对象上调用成员函数 fetch()
因此,似乎当第一次执行失败(id 为 293 的那个)时,事务会自动中止...... PDO 是自动回滚还是其他?
我的目标是完成第一个大的 while 循环,最后,使用 bool var 作为标志,决定是回滚还是提交事务。