9

这是此问题的特定版本。
我想检查我是否插入了重复的行。我应该在我的应用程序层中以编程方式检查它:

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

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

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

编辑:换句话说:虽然约束仍然存在(无论如何它是很好的数据库设计,我不能确定我的应用程序将是唯一访问表的应用程序)我是否应该依赖约束并处理异常它的违规会加注,还是我还是过牌?

EDIT2:当然我在事务中进行检查+插入,锁定表以确保同时没有其他进程正在写入另一条记录

4

7 回答 7

8

首先,您必须在数据库上有一个主键或唯一约束,才能正确地强制执行这种唯一性——毫无疑问。

鉴于存在约束,您应该以哪种方式在应用程序中编码?我的偏好是尝试插入并捕获异常。因为可能大多数插入都会成功,只有少数会因为重复而失败(这就是“异常”的含义!):在每次插入之前执行存在检查是低效的,因为无论如何数据库都将执行自己的约束检查。

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

于 2008-10-03T14:44:03.380 回答
2

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

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

我对这个问题的最初理解是,在选项 A 中,重复检查将在内部执行(即仅使用程序已经创建的数据结构,并且在插入之前没有查询)。我最初的答案是对这种方法的回应。

于 2008-10-03T14:37:45.280 回答
2

您检查该对象是否仅存在于应用程序代码中,然后一旦确定它不存在,就愉快地保存该对象。但是另一个并发客户端可能会在您的两行代码之间插入他们自己的对象。所以无论如何你都会得到一个重复的异常,只是这次你没有捕捉到它。

您必须执行 save() 并捕获异常。否则,您将与其他在同一数据库上工作的并发客户端发生争用情况。

于 2008-10-03T16:13:25.710 回答
1

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

于 2008-10-03T14:36:25.220 回答
1

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

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

于 2008-10-03T14:56:14.960 回答
1

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

如果异常有足够的信息,您可以生成一条真正帮助用户的错误消息,那么只需捕获异常,分析它,然后继续。

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

问题是“例外有多不透明”之一?有些非常不透明。其他人有足够的能力来解析消息字符串并弄清楚要对用户说什么。

于 2008-10-03T15:09:10.990 回答
0

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

此外,第一个片段中的代码可能会导致另一个进程插入一条记录,这会导致在您检查重复记录的时间和实际插入它的时间之间引发重复异常。

于 2008-10-03T15:11:02.327 回答