當業務邏輯涉及多張表的寫入操作(如註冊初始化多張關聯表、問答回覆同時更新問題與回答表),單條 SQL 失敗會導致資料不一致。MySQL 事務透過「全成功提交、一失敗回滾」特性完美解決此問題。本文提供基於 mysqli 的事務封裝類方法。
/**
* 批量 SQL 事務執行
* @param string $password 資料庫密碼(或配置識別)
* @param array $sqls SQL 陣列
* @return bool 全部成功 true / 任一失敗 false
*/
public function doArraySqlActionsTran($password, $sqls) {
$db = $this->doSqlLink($password); // 建立 mysqli 連線
$db->autocommit(FALSE); // 關閉自動提交,開啟事務
$results = [];
foreach ($sqls as $sql) {
$results[] = $db->query($sql); // 執行每條 SQL
}
// 檢查執行結果
$hasFailure = false;
foreach ($results as $res) {
if ($res === false) {
$hasFailure = true;
break;
}
}
if ($hasFailure) {
$db->rollback(); // 回滾所有操作
$db->autocommit(TRUE); // 恢復自動提交
$db->close();
return false;
} else {
$db->commit(); // 提交事務
$db->autocommit(TRUE);
$db->close();
return true;
}
}
| 步驟 | 方法 | 說明 |
|---|---|---|
| 1. 關閉自動提交 | autocommit(FALSE) | MySQL 預設每條 SQL 立即提交,必須關閉才能進入事務模式 |
| 2. 執行 SQL 群 | query() | 依序執行,若中途失敗後續仍會執行(除非手動中斷),最終由結果判斷 |
| 3. 結果判斷 | 遍歷結果陣列 | 任一 false 即視為失敗 |
| 4. 回滾/提交 | rollback() / commit() | 原子性保證:要麼全做,要麼全不做 |
| 5. 恢復自動提交 | autocommit(TRUE) | 釋放連線回連線池時恢復預設行為 |
prepare() + bind_param() 防 SQL 注入並提升重複執行效能。$db->error、失敗的 SQL 及參數,便於事後排查。mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT) 拋出異常,使用 try-catch 結構更清晰。mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
$db = new mysqli($host, $user, $pass, $dbname);
$db->begin_transaction(); // 等同 autocommit(FALSE)
foreach ($sqls as $sql) {
$db->query($sql);
}
$db->commit();
return true;
} catch (mysqli_sql_exception $e) {
$db->rollback();
error_log('Transaction failed: ' . $e->getMessage());
return false;
} finally {
$db->close();
}