在尝试构建健壮的数据库代码(表锁定,事务等)时,我总是被需要完成的大量代码所困扰。
例如,两个准备好的语句中的一个事务,我想删除一个用户并在“actions”表中更新一些关于他的内容:
我是这样做的(也许我做错了?)。如果我这样做它的很多工作和烦人。所以我认为自动化这些东西。
我想要的是这样的:
$DB->startTransaction();
$DB->query($query);
$DB->query($query2);
$DB->query($query3);
$DB->endTransaction();
在内部,mysqli上面的数据库抽象层将负责表锁定,预处理语句和事务本身。我们不应该自动化吗?
这是我的一次尝试:
public function query($query, $table, $params = null) {
if($params == null) {
$this->connection->query("LOCK TABLES $table WRITE");
$query = str_replace("!", $table, $query);
$result = $this->connection->query($query);
$this->connection->query("UNLOCK TABLES");
return $result;
}
else {
if (!$this->checkParams($query, $params)) {
return false;
}
$this->connection->query("LOCK TABLES $table WRITE");
$query = str_replace("!", $table, $query);
$stmt = $this->connection->prepare($query);
if ($stmt != false) {
$typesString = "";
foreach ($params as $param) {
if (is_numeric($param)) {
$typesString .= "i";
} else if (is_double($param)) {
$typesString .= "d";
} else {
$typesString .= "s";
}
}
$finalParamArray = array($typesString);
$finalParamArray = array_merge($finalParamArray, $params);
call_user_func_array(array($stmt, "bind_param"), $this->ref($finalParamArray));
$this->checkStatement($stmt);
$stmt->execute();
$this->checkStatement($stmt);
$result = $stmt->get_result();
$stmt->close();
$this->connection->query("UNLOCK TABLES");
return $result;
}
$this->query("UNLOCK TABLES");
return false;
}
}
这可以这样调用:
$DB->query("DELETE FROM ! WHERE userID =?", "Users", array($userID));
但我对此并不自信。我google了一下,没有找到像我想要的东西。所以现在我的问题是:我想要的实际可能(它应该是)吗?我做错了吗?
编辑: 我还有另外两次尝试这样做,看起来更复杂(300多行代码)。如果你愿意,我也可以发布它们。然而,我仍然对他们不满意,并且不确定这是否真的正确!
答案 0 :(得分:1)
您是对的,应该有一个更简单的方法来执行此操作,并且您也正确地说我们需要在mysqli之上的抽象层。它不能单独使用。
您不需要太多步骤。特别是,您无需检查每种方法的返回码。那应该已经消除了6个或更多的步骤。您也不需要关闭语句。
绑定时无需指定类型。一直使用字符串类型。其他类型很少派上用场,几乎从未派上用场。
前一段时间,我发布了一个示例,说明mysqli之上的抽象层是什么样的。
class DBClass extends mysqli {
public function __construct(
$host = null,
$username = null,
$passwd = null,
$dbname = null,
$port = null,
$socket = null
) {
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
parent::__construct($host, $username, $passwd, $dbname, $port, $socket);
$this->set_charset('utf8mb4');
}
public function safeQuery(string $sql, array $params = []): ?array {
$stmt = $this->prepare($sql);
if ($params) {
$stmt->bind_param(str_repeat("s", count($params)), ...$params);
}
$stmt->execute();
if ($result = $stmt->get_result()) {
return $result->fetch_all(MYSQLI_BOTH);
}
return null;
}
}
这远非完美,但它显示了主要思想。您可以将准备好的语句包装在一个方法中。简单的准备/绑定/执行/获取结果。而已。它可以使用和不使用参数。
在构造函数中,打开连接的3个必需步骤:切换错误报告,创建mysqli实例以及设置正确的字符集。
如果要进行交易,则可以使用mysqli的begin_transaction()
和commit()
。它们足够简单,不需要抽象。
我不知道您为什么觉得需要锁定表,但这又是一个简单的SQL语句,不需要抽象。
$db = new DBClass('localhost', 'user', 'pass', 'test');
$db->safeQuery('LOCK TABLES users WRITE');
$db->begin_transaction();
$db->safeQuery('DELETE FROM users WHERE userID =?', [$userID]);
$db->safeQuery('DELETE FROM otherTable WHERE userID =?', [$userID2]);
$db->commit();
$db->safeQuery('UNLOCK TABLES');