为何PHP PDO会忽略MariaDB的NOT NULL约束?
解决PDO插入MariaDB违反NOT NULL约束不抛异常的问题
你的场景中,person表的alias1字段定义为NOT NULL且无默认值,但通过PDO执行省略该字段的INSERT语句时,数据库自动插入了空字符串而非抛出错误,导致PDO未触发异常。直接用SQL脚本执行相同操作却能正常报错,核心原因是PDO连接会话的sql_mode未启用严格模式。
原因分析
MariaDB/MySQL在非严格模式下,遇到NOT NULL字段未赋值的情况,会对字符串类型字段自动填充空字符串、数值类型填充0,而不会抛出约束违反错误。此时数据库返回的是成功状态,PDO即便设置了ERRMODE_EXCEPTION也不会触发异常。而你直接用SQL脚本执行时,当前会话的sql_mode可能已经启用了严格模式,所以会报错。
解决方案
在PDO连接时,强制设置会话的sql_mode为严格模式(至少包含STRICT_TRANS_TABLES,这是InnoDB表的严格模式核心参数),让数据库在违反约束时返回错误,从而触发PDO的异常机制。
实现方式
有两种常用方法:
- 在PDO初始化命令中添加
sql_mode设置 - 连接成功后单独执行
SET sql_mode语句
方法1:修改PDO连接的初始化命令
修改PDO构造函数的MYSQL_ATTR_INIT_COMMAND参数,合并编码设置与严格模式设置:
$conn = new PDO( 'mysql:host=localhost;dbname=tibsi-data', 'root', '', array( PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4; SET sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION'" ) );
注:把SET NAMES utf8改成utf8mb4,与你的表编码保持一致,避免编码不一致问题。
方法2:连接后执行设置语句
如果初始化命令存在长度限制(部分环境下会有),可以在连接成功后单独执行设置:
$conn = new PDO( 'mysql:host=localhost;dbname=tibsi-data', 'root', '', array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4") ); $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // 设置严格模式 $conn->exec("SET sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION'");
修改后的完整测试脚本
<?php $conn = new PDO( 'mysql:host=localhost;dbname=tibsi-data', 'root', '', array( PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4; SET sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION'" ) ); $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $conn->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); try { $stm = $conn->prepare("INSERT INTO person (firstname) VALUES (:firstname)"); $stm->bindValue(':firstname', 'Mike'); // 无需判断execute返回值,ERRMODE_EXCEPTION会自动抛出异常 $stm->execute(); $stm->closeCursor(); $insertId = $conn->lastInsertId(); header('HTTP/1.1 200 OK'); header('Content-Type: application/json; charset=utf-8'); echo json_encode(array('id' => $insertId)); exit; } catch (Exception $e) { header('HTTP/1.1 500 Internal Server Error'); header('Content-Type: application/json; charset=utf-8'); echo json_encode(array('error' => $e->getMessage())); exit; } catch (Error $e) { header('HTTP/1.1 500 Internal Server Error'); header('Content-Type: application/json; charset=utf-8'); echo json_encode(array('error' => $e->getMessage())); exit; }
验证效果
修改后执行脚本,数据库会因alias1未赋值违反NOT NULL约束返回错误,PDO会捕获该错误并抛出异常,最终返回500响应和错误信息,符合预期。
内容的提问来源于stack exchange,提问作者Lycodo
相关产品推荐
相关产品推荐

