MySQLi预处理语句重复键错误无法捕获,需自定义提示
MySQLi预处理语句更新用户时捕获唯一约束冲突错误方案
问题背景
使用MySQLi预处理语句执行用户信息更新操作,username字段设置唯一约束。输入重复用户名时,无法触发代码else分支的自定义错误提示,反而抛出致命错误:Uncaught mysqli_sql_exception: Duplicate entry 'admin' for key 'username_2'。此前非预处理语句可正常触发错误提示,尝试通过mysqli_errno == 1062判断无效,仅成功分支能正常跳转。
根因分析
数据库连接代码中开启了严格错误报告模式:
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
该模式会将MySQL的所有错误抛出为mysqli_sql_exception异常,而非传统的返回错误码。因此执行$stmt->execute()遇到唯一约束冲突时,不会进入else分支,而是直接抛出异常中断代码执行,这是与非预处理语句(未开启严格模式时)的核心差异。
解决方案
通过异常捕获机制处理约束冲突错误,同时加入事务保证多操作的原子性,修改后的代码如下:
更新操作代码
<?php session_start(); include("../../functions/connectDatabase.php"); if(isset($_POST['updateadviserdata'])) { // 开启事务,保证两个操作原子性 $mysqli->begin_transaction(); try { // 预处理语句无需手动转义,参数绑定自动处理SQL注入 $id = $_POST['update_id']; $fn = $_POST['fname']; $mn = $_POST['mname']; $ln = $_POST['lname']; $un = $_POST['username']; // 执行用户信息更新 $stmt = $mysqli->prepare("UPDATE users SET `firstname`= ?,`middlename`= ?, `lastname`= ?, `username` = ? WHERE `user_id` =?"); $stmt->bind_param("ssssi", $fn, $mn, $ln, $un, $id ); $stmt->execute(); $stmt->close(); // 插入操作日志 $stmtUA = $mysqli->prepare("INSERT into history (activity,user_id,date_added) VALUES ('update name',?,NOW() )"); $stmtUA->bind_param("i", $id); $stmtUA->execute(); $stmtUA->close(); // 提交事务 $mysqli->commit(); $_SESSION['success']= "用户名修改成功"; header('Location: ../../adviseraccount.php'); exit; } catch(mysqli_sql_exception $e) { // 回滚事务,避免部分操作生效 $mysqli->rollback(); // 判断是否为唯一约束冲突(错误码1062) if($e->getCode() == 1062) { $_SESSION['error'] ="用户名已存在"; } else { // 其他数据库错误可自定义提示或记录日志 $_SESSION['error'] = "操作失败:" . $e->getMessage(); } header('Location: ../../adviseraccount.php'); exit; } }
关键优化点
- 事务处理:用
begin_transaction()、commit()、rollback()保证UPDATE和INSERT操作要么全部成功,要么全部回滚,避免数据不一致。 - 异常捕获:通过
try-catch捕获mysqli_sql_exception,在catch块中判断错误码1062,触发自定义提示。 - 移除冗余操作:删除
real_escape_string调用,预处理语句的参数绑定已自动处理SQL注入,无需手动转义。 - 跳转后终止代码:
header()后调用exit,防止后续代码继续执行。
内容的提问来源于stack exchange,提问作者StoicHooman
相关产品推荐
相关产品推荐

