我的代码在插入远程数据库时偶尔会遇到唯一的索引冲突。由于我需要随机生成但人类可读的值,因此无法避免违规。不幸的是,当发生这种情况时,PDO只抛出一个泛型的PDOException,所以我有一个包含这种可憎的循环:
do {
...
} catch (PDOException $e) {
if ($e->getCode() === '23000') {
continue; // have another go
} else {
throw new Exception("...", 1, $e); // reraise
}
} while(there was an exception);
我认为错误捕获将比搜索然后插入更好,因为使用四位数代码并且在任何时候只有几百个活动代码会有很少的冲突。
像best practices for catching and re-throwing exceptions?和Rethrow php exception into higher level catch block这样的问题表明,即使在PHP中,这也是不好的做法。我都抓到了一个例外我99%肯定我要重新抛出,我正在使用异常进行流量控制。
有没有更好的方法在PHP中执行此操作,或者是我在数据库中使用存储过程的唯一替代方法,该存储过程在失败时返回错误代码而不是异常。
答案 0 :(得分:3)
我的结论是,在PHP中确实没有更好的方法可以做到这一点,所以我已经将逻辑移到MySQL中,它稍微不那么难看。我使用错误处理而不是搜索,因为对于没有碰撞的正常情况,这会更快。
在PHP(两个或更多个查询)中首先进行搜索会慢得多,而对于SQL中的正常情况(一个额外查询)来说稍慢一些,但如果有很多冲突,那么执行起来会更快首先搜索而不是插入失败。
DECLARE CONTINUE HANDLER FOR 1062 -- duplicate key
SET @dupe = 1;
SET @dupe = 0;
SET @attemptsLeft = 5;
REPEAT
SET @user_code = randomString(4);
INSERT INTO mytable(expires, user_code, users_id)VALUES(p_expires, @user_code, p_users_id);
SET @attemptsLeft = @attemptsLeft - 1;
-- if there's a dupe we get the continue handler above
UNTIL @dupe = 0 OR @attemptsLeft <= 0 END REPEAT;
答案 1 :(得分:2)
如果你想重新抛出相同的异常,为什么不使用:
throw $e;