这是此问题的特定版本。

我想检查是否插入了重复的行。我应该在我的应用程序层中以编程方式检查它:

if (exists(obj))
{
    throw new DuplicateObjectException();
}
HibernateSessionFactory.getSession().save(obj);

或者我应该捕获数据库层引发的异常并在违反约束时触发?

try
{
    HibernateSessionFactory.getSession().save(obj);
}
catch(ConstraintViolationException e)
{
    throw new DuplicateObjectException();
}

编辑:换句话说:虽然约束仍然存在(无论如何都是好的数据库设计,我不能确定我的应用程序将是唯一一个访问该表的应用程序)依靠约束并处理违规将引发的异常,或者我最好还是检查一下?

EDIT2:当然我在事务中检查+插入,锁定表以确保其他进程在此期间没有写入其他记录

有帮助吗?

解决方案

首先,您必须在数据库上拥有主键或唯一约束才能正确实施此唯一性 - 毫无疑问。

鉴于存在约束,您应该在应用程序中编写哪种方式?我的偏好是尝试插入并捕获异常。因为大概大多数插入都会成功,只有少数会因为重复而失败(这就是“异常”所暗示的!):在每次插入之前执行存在检查是无效的,当数据库要执行自己的约束检查时

此外,理论上可能存在检查错误 - 如果其他人设法在您的存在检查和插入之间的小间隔内提交具有相同键值的记录。然后,如果你没有捕获数据库异常,你会相信插入成功,而事实上它没有。

其他提示

您检查对象是否仅存在于应用程序代码中,然后一旦确认它不存在,则轻率地保存该对象。但是另一个并发客户端可能会在您的两行代码之间插入自己的对象。所以,无论如何你都会得到一个重复的例外,只有这次你没有抓住它。

您必须执行save()并捕获异常。否则,您在同一数据库上处理其他并发客户端的竞争条件。

您需要捕获数据库异常,除非您可以保证您的应用程序是唯一一个将行(并且将会插入行)插入数据库的应用程序。

编辑:我可能误解了这个问题,但我仍然认为选项B(HibernateSessionFactory从数据库中抛出ConstraintException)是更好的选择。另一个应用程序在检查和实际函数调用之间的时间间隔内插入一些东西的可能性很小。此外,检查欺骗的唯一方法是执行额外的查询,这只是不必要的性能消耗。

我对该问题的原始理解是,在选项A中,欺骗检查将在内部执行(即仅使用程序已创建的数据结构,并且在INSERT之前不进行查询)。我原来的回答是对这种方法的回应。

一般情况下,我会尽量避免依赖于抛出错误的编码因为我做错了。但有时候,这就是你所能做的一切。在你的情况下,我认为你应该先检查一下。

如果由于某种原因(通常是DBA忽略重新启用它的维护工作),约束会被删除,这将中断(允许重复条目)。您应该在应用程序中检查这种情况。

但是,让数据库强制执行约束(正如您已正确指出的那样)是一个很好的数据库设计,因为其他人也可能正在使用数据库。作为概括,最好假设应用程序和数据库存在于M:M关系中 - 几乎所有时间都是如此。

Hibernate(或任何ORM组件)抛出的异常往往难以解释。

如果异常有足够的信息可以产生实际帮助用户的错误消息,那么只需捕获异常,分析异常,然后继续。

如果异常没有足够的信息,那么您必须检查错误情况,并向用户发出有用的错误消息,告知他们他们做错了什么。

问题是“异常是多么不透明”之一?有些是非常不透明的。其他人已经足够你可以解析消息字符串并找出对用户说什么。

一旦hibernate从会话中抛出异常,您需要必须丢弃会议(见第11.2.3节)。因此,如果您需要检查重复并继续使用相同的会话,那么您别无选择,只能先检查应用程序。

另外,第一个代码段中的代码有可能另一个进程可能会插入一条记录,该记录会导致在检查重复记录和实际插入时间之间抛出重复异常。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top