我该在代码中检查数据库约束条件还是捕获由数据库引发的异常?

21

我有一个应用程序,将数据保存到名为 Jobs 的表中。Jobs 表中有一个名为 Name 的列,该列具有唯一约束条件。Name 列不是主键。我想知道在尝试保存/更新新条目之前,是否应该自己检查重复条目,还是最好等待数据访问层抛出的异常。如果很重要,我正在使用NHibernate进行此应用程序。


感谢大家的帮助。

我发现另一个原因,为什么我应该在代码中进行验证,而不仅仅是等待抛出异常(并被我的代码捕获)。似乎 NHibernate 只会抛出一个不太详细的 NHibernate.Exceptions.GenericADOException 异常,这在这种情况下并不是很有信息量。或者我错过了 NHibernate 的某个方面吗?

8个回答

17
答案是:两者都是。如果您的数据库具有约束条件,它可以保证数据的某些不变量,例如唯一性。这在以下几个方面有所帮助:
- 如果应用程序存在错误并违反了约束条件,则会标记出可能未被注意到的问题。 - 数据库的其他用户可以假定数据的行为更加确定,因为DBMS执行不变量。 - 数据库通过强制执行约束条件来保护自身免受违反约束条件的不正确更新。如果您发现将来有其他系统或接口填充数据库,数据库施加的约束意味着任何由约束条件捕获的内容都不会(或至少不太可能)破坏您的系统。
在除最简单情况之外的任何情况下,应用程序和数据库都处于M:M关系。应用程序仍应具有适当的数据和业务规则验证,但您仍不应该计划您的应用程序是数据的唯一使用者。在数据仓库工作几年后,您将看到由持有这种心态的人设计的应用程序所产生的影响。

4
如果你的设计很好(包括数据库和业务逻辑),那么数据库不应该有任何未在业务逻辑中处理的约束,也就是说,你不应该向数据库提供不一致的数据。但是没有什么是完美的。
我发现将数据库限制为数据一致性约束可以让我在过程化代码中处理所有业务逻辑验证,而我遇到数据库异常的唯一情况是设计和编码错误,这些错误可以(也应该)被修复。
在你的情况下,检查名称是否唯一是数据内容验证,在代码中得到了正确处理。这样做可以在最接近出错点的地方捕获错误,而且你还可以利用更友好的用户界面资源,而不会引入不必要的抽象耦合。

3

我建议完全将该工作交给数据库; 你的代码应该专注于捕获并正确处理异常。

原因:

  1. 性能- 数据库会高度优化以快速高效地强制执行约束条件。你不必花时间去优化你的代码。
  2. 可维护性- 如果将来约束条件发生变化,你无需修改代码,或者只需添加一个新的catch{}。如果一个约束被删除,你根本不需要触及你的代码。

5
有些应用程序需要向用户准确地指出出了什么问题。如果您想要类似“已经存在用户名为‘Joseph’的用户”或“登录名‘bw’已被用户‘布鲁斯·威利斯’使用”等异常,您不能仅在catch处理程序中完成此操作,因为那里没有所有信息,或者它是太特定于数据库了。 - Stefan Steinegger

2

如果您要自己检查约束条件,请在数据访问层中进行。上面的任何层都不应该知道关于您的数据库或其约束条件的任何信息。

在大多数情况下,我会说将DB来源的异常留给DAL来处理。但在您的特定情况下,我认为我们正在谈论基本的输入验证。我会选择在提交整个表单之前调用数据库进行名称可用性检查。


1
你需要回答的问题是:
“我需要向用户展示友好的消息吗?”例如:已经存在一个名为TestJob1的作业。 如果答案是不需要,只需捕获错误并呈现常见消息 如果答案是需要,请继续阅读

如果你在插入后捕获错误,那么没有足够的信息来呈现正确的消息(至少在DB中是这样的)

另一方面,可能会出现竞争条件,多个事务同时尝试插入相同的数据,因此您需要DB约束

一个有效的方法是:

  • 在呈现友好消息之前进行检查
  • 捕获异常并呈现常见错误消息(假设这种情况不会经常发生)

1

你应该确保检查数据访问层抛出的任何异常。检查是否存在具有相同值的记录的问题在于,它要求您锁定表以防止竞争条件,直到插入新记录。

通常建议即使您已经自己检查了所有内容,也要检查异常/错误。几乎总会有一些事情可能会出错或者您没有考虑到但是数据库强制执行的东西。

编辑:如果我正确理解了问题,那么问题不在于是否应该由数据库强制执行约束,而在于如何在应用程序代码中处理它。当然,您应该始终在数据库中设置所有约束,以防止不良数据进入您的数据库。


是的,你理解得没错。我在问当我知道异常会被抛出时,是否可以避免访问数据库来检查重复性。 - trendl
但是要进行检查,您也需要查询数据库,因此您甚至必须发出(至少)两个查询:一个用于检查,另一个用于插入/更新。假设重复很少,这比仅尝试插入并捕获异常更加昂贵。 - Simon Lehmann

0

个人而言,我会捕获异常。这样更简单,需要的代码也少得多。


0

GenericADOException的内部异常将告诉您为什么数据库操作失败。您可以捕获OracleException / MSSQLException / [InsertCustomExceptionHere]并从该消息处理错误。如果您想将其传递回前端(假设用户是输入重复数据的人),则可能需要首先将其包装在自定义异常中,以便您不会将前端与数据库耦合。您真的不希望传递特定于RDBMS的异常。

我不同意在执行插入之前检查唯一性,往返两次到数据库效率不高,如果您有大量用户流量,则肯定不可扩展。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接