关于java:哪个更好 – 抛出异常或事先检查错误

Which is better - Throwing an Exception or Checking for errors beforehand

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


您应该执行"try-and-catch异常"方法,因为您无论如何都必须这样做。

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

没有能够在某种事务中运行检查和插入(以便其他人无法在临时插入该用户)。你无法确定非例外是否有效。

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

当然,如果您的应用程序设计为只有您的进程将插入用户(并序列化),您可以使用check-first方法。但是我会发表大量的评论意见,如果你扩大规模,就需要重新审视。


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

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

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

如果你想要回答:

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

仅当用户名尚不存在时,才会返回该用户名。
但是,该操作不是原子操作,因此如果您有很多并发,请在表上获取锁定,如下所示:

1
2
3
4
5
6
7
8
9
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;

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

  • 汤姆里对此事。
  • 关于锁的手册。


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

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

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

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

  • 特殊错误是否真的例外?
  • C ++:在非特殊情况下是否有理由使用异常
  • Juding是否异常是例外
  • 为什么使用异常而不是if ... else


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


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

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

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


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

Or just insert the username in the table. If it already exists, then
it will throw an error which can then be caught

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


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


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


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

PostgreSQL自动增量