哪个更好 - 抛出异常或事先检查错误

时间:2012-03-15 09:05:50

标签: java postgresql exception-handling

在我的服务器上,连接到postgresql,我应该通过执行"select * ..."检查表中是否已存在用户名,然后获取结果集中的行数,并且我的行数等于零,然后插入用户名?
或者只是在表格中插入用户名。如果它已经存在,那么它将抛出一个错误,然后可以捕获它 注意:用户名是主键


做上面哪两个更好?

9 个答案:

答案 0 :(得分:4)

你应该仅仅因为你必须这样做而进行“尝试并捕获异常”方法。

如果你先检查一下,没有什么可以阻止某人在你的支票和你的插入物之间为该用户插入一行,在这种情况下,即使你的支票没找到,用户也会在表格中。

无法在某种事务中运行检查和插入(因此没有其他人可以在此期间插入该用户)。你无法确定非例外是否有效。

尽管许多DBMS提供了事务支持,但我不知道任何会锁定你尚未插入的行: - )

当然,如果您的应用程序的设计方式只允许您的进程插入用户(并序列化),则可以使用check-first方法。但是,如果你扩大规模的话,我会发表大量的意见,说明它需要重新审视。

答案 1 :(得分:2)

通常的共识是仅对例外案例使用例外,而不是作为控制流构造。在我看来,尝试使用恰好采用的用户名应该被认为是有效的,而不是那种不常见的用例。

换句话说,我会首先检查现有的用户名。

正如@paxdiablo指出的那样,如果您处于多线程环境中,例如Web服务器,那么您需要添加一些锁定方案或者仍然使用try / catch方法(考虑到两个线程可能正在竞争添加相同的用户名)。 然而,情况可以被视为例外情况。

相关问题(所有结论相同,不对异常情况使用例外情况):

答案 2 :(得分:1)

在这种情况下,答案是既不。既不会引发错误,也不会事先检查。那么主要是,无论如何 它可以更简单地处理 - 同时更安全,更快捷:

INSERT INTO users(username, col1)
SELECT 'max', 'val1'
WHERE  NOT EXISTS (SELECT * FROM users WHERE username = 'max')

仅当新用户不存在时才会插入新用户。 PostgreSQL会将命令状态设置为0 rows affected1 row affected,具体取决于它是否已存在。无论哪种方式,它都会在此声明之后出现。

如果你想回答:

INSERT INTO users(username, col1)
SELECT 'max', 'val1'
WHERE  NOT EXISTS (SELECT * FROM users WHERE username = 'max')
RETURNING username;

仅当用户名尚不存在时,才会返回该用户名 但是,操作不是原子操作,所以如果你有很多并发,就像这样获取表上的锁:

BEGIN;
LOCK TABLE users IN SHARE MODE;

INSERT INTO users(username, col1)
SELECT 'max', 'val1'
WHERE  NOT EXISTS (SELECT * FROM users WHERE username = 'max')
RETURNING username;

COMMIT;

请注意,这仍然可能失败,即使非常不可能 - 例如,如果另一个事务锁定表并由于某些错误而永远阻止您。 所以,诚然,你仍然需要代码来处理错误情况。除非您的数据库或应用程序出现问题,否则它永远不会发生。

答案 3 :(得分:0)

我想说在这种情况下捕获异常是滥用异常概念,如果你之前可以检查它,你应该检查它。

答案 4 :(得分:0)

永远不应该使用异常来控制程序的流程。如果不是例外情况,最好避免例外。

答案 5 :(得分:0)

我更希望检查用户是否通过查询完成,而不是使用异常。 “用户存在”错误的逻辑很快就会成为业务规则。 (你可以在SQL中编写这样的规则,但那是完全不同的世界)

  

或者只是在表格中插入用户名。如果它已经存在,那么   它会抛出一个可以被捕获的错误

问题是有许多其他原因导致例外。无论如何你必须处理它们。

答案 6 :(得分:0)

您将获得许多答案,其中包括“永远不应该使用异常来控制程序的流程”和“异常不适用于控制流程”。的确,你已经有几个。你可能像我一样,发现这些陈述完全没有意义。例外控制程序的流程,当API被设计为抛出异常时,您没有任何选择,只能相应地使用它。 EOFException就是一个很好的例子。当调用抛出它的方法时,你没有任何其他方法来检测EOS。

在这种情况下,适用不同的原则。如果你测试然后设置,你将引入一个时间窗口,在此期间后续的设置可能会失败,如果set操作可以抛出异常,你必须为它编码。在这些情况下,您应该只进行设置并相应地处理异常。这样你的操作就是原子的,你不必两次编写相同的代码。通常,检测资源是否可用的最可靠方法是尝试使用它(考虑连接到网络服务器),并且检测操作是否会失败的最可靠方法是实际尝试它(考虑这种情况,即将值插入到数据结构中,在该数据结构中,它是唯一键。

关于“异常永远不应该用于控制程序流程”的规则最初来自一个更窄的上下文,这意味着你通常不应该抛出你在同一方法中捕获的异常,即将它们用作一种GOTO。然而,正如在这个行业中非常普遍的那样,最初的动机完全被遗忘了,我只能将其描述为无意识的,鹦鹉式的,重复的。

答案 7 :(得分:0)

如果直接插入用户名而不进行任何检查,并假设数据库中存在相同的用户名,那么您将获得异常,否则它将在数据库中成功插入记录。根据标准编码实践(在我看来),您应首先检查唯一性,如果数据库中不存在用户名,则在数据库中插入记录。

答案 8 :(得分:-1)

正确的做法不是关于你的情况下的异常处理,你应该使用自动递增的主键。

PostgreSQL Autoincrement