逻辑:数据库或应用程序/2(约束检查)
这是此问题的特定版本。
我想检查是否插入了重复的行。 我应该在应用程序层以编程方式检查它:
if (exists(obj))
{
throw new DuplicateObjectException();
}
HibernateSessionFactory.getSession().save(obj);
还是应该捕获数据库层抛出的异常并在违反约束时触发?
try
{
HibernateSessionFactory.getSession().save(obj);
}
catch(ConstraintViolationException e)
{
throw new DuplicateObjectException();
}
编辑:换句话说:虽然约束仍然存在(无论如何,这是很好的数据库设计,而且我不能确定我的应用程序将是唯一访问该表的应用程序)我应该依赖约束并处理其违规将引发的异常,或者我最好还是检查一下?
编辑2:当然,我在事务中进行检查+插入,锁定表以确保没有其他进程同时写入另一条记录
This is a specific version of this question.
I want to check if I am inserting a duplicate row. Should I check it programmatically in my application layer:
if (exists(obj))
{
throw new DuplicateObjectException();
}
HibernateSessionFactory.getSession().save(obj);
or should I catch the exception thrown by the database layer and triggered when I violate the contraint?
try
{
HibernateSessionFactory.getSession().save(obj);
}
catch(ConstraintViolationException e)
{
throw new DuplicateObjectException();
}
EDIT: In other words: though the constraint is there to remain (it's good database design anyway, and I can't be sure my app will be the only one accessing the table) shall I rely on the constraint and handle the exception its violation will raise, or I'd better check anyway?
EDIT2: Of course I do check+insert within a transaction, locking the table to ensure no other process is writing another record in the meantime
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
首先,您必须在数据库上有一个主键或唯一约束,以正确强制执行这种唯一性 - 毫无疑问。
鉴于存在约束,您应该在应用程序中以哪种方式进行编码? 我的偏好是尝试插入并捕获异常。 因为大概大多数插入都会成功,只有少数会因为重复而失败(这就是“异常”所暗示的!):当数据库无论如何都要执行自己的约束检查时,在每次插入之前执行存在检查效率很低。
此外,理论上,存在检查无论如何都可能是错误的 - 如果其他人设法在存在检查和插入之间的一小段时间内提交具有相同键值的记录。 然后,如果您不捕获数据库异常,您会认为插入成功,但实际上没有成功。
First, you must have a primary key or unique constraint on the database to enforce this uniqueness properly - no question.
Given that the constraint exists, which way should you code in the application? My preference would be to try the insert and catch the exceptions. Because presumably most inserts will succeed, only a few will fails as duplicates (that's what "exception" implies!): it is inefficient to perform an exists check before every insert, when the database is going to be performing its own constraint checking anyway.
Also, it is theoretically possible for the exists check to be wrong anyway - if someone else manages to commit a record with the same key value in the small interval between your exists check and your insert. Then, if you don't trap the database exception, you will believe the insert succeeded when in fact it didn't.
您检查该对象是否仅存在于应用程序代码中,然后一旦确认它不存在,就可以轻松地保存该对象。 但是另一个并发客户端可能会在您的两行代码之间插入自己的对象。 所以无论如何你都会得到一个 Duplicate 异常,只是这次你没有捕获到它。
您必须执行 save() 并捕获异常。 否则,您将与在同一数据库上工作的其他并发客户端产生竞争条件。
You check that the object exists solely in application code, and then once satisfied that it does not, blithely save the object. But another concurrent client might insert their own object in the moment between your two lines of code. So you'd get a Duplicate exception anyway, only this time you don't catch it.
You must do the save() and catch the exception. Otherwise you have a race condition with other concurrent clients working on the same database.
您需要捕获数据库异常,除非您可以保证您的应用程序是唯一向数据库中插入行(并且将插入行)的应用程序。
编辑:我可能误解了这个问题,但我仍然认为选项 B(HibernateSessionFactory 从数据库抛出 ConstraintException)是更好的选择。 另一个应用程序总是有可能在您的检查和实际函数调用之间的一小段时间内插入某些内容。 此外,检查重复的唯一方法是执行额外的查询,这只会不必要地消耗性能。
我对这个问题的最初理解是,在选项 A 中,重复检查将在内部执行(即仅使用程序已经创建的数据结构,并且在插入之前不进行查询)。 我原来的回答是针对这个方法的。
You need to catch the database exception unless you can guarantee that your application is the only one that ever inserts rows (and ever will insert rows) into your database.
EDIT: I may have misunderstand the question, but I would still argue that option B (HibernateSessionFactory throws the ConstraintException from the database) is the better option. There's always a small chance that another application could insert something in the sliver of time between your check and the actual function call. In addition, the only way to check for a dupe is to perform an additional query which is just a needless drain on performance.
My original understanding of the question was that in option A the dupe check would be performed internally (i.e. by using only the data structures that the program had already created, and with no query until the INSERT). My original answer was in response to this method.
一般来说,我会尽量避免因为我做错了什么而依赖于抛出错误的编码。 但有时,这就是你所能做的一切。 对于你的情况,我觉得你应该先检查一下。
In general, I try to avoid coding that relies on errors being thrown because I did something wrong. Sometimes, though, that's all you can do. In your situation, I think you should check first.
如果约束因某种原因被删除(通常是 DBA 忽略重新启用它的维护工作),这将会中断(允许重复条目)。 您应该在应用程序中检查这种情况。
然而,让数据库强制执行约束(正如您非常正确地指出的那样)是很好的数据库设计,因为其他人也可能正在使用该数据库。 作为概括,最好假设应用程序和数据库存在 M:M 关系 - 几乎所有时间都是这种情况。
This will break (allowing duplicate entries) if the constraint gets dropped for some reason (typically maintenance work where the DBA neglects to re-enable it). You should check for this situation within the application.
However, it is good database design to have the database enforce the constraint (as you have quite rightly pointed out) as others may also be using the database. As a generalisation it is best to assume that applications and databases live in a M:M relationship - this will be the case almost all of the time.
Hibernate(或任何 ORM 组件)抛出的异常往往难以解释。
如果异常有足够的信息,您可以生成一条实际上对用户有帮助的错误消息,那么只需捕获异常,分析它,然后继续。
如果异常没有足够的信息,那么您必须检查错误条件,并向用户生成一条有用的错误消息,表明他们做错了什么。
问题是“例外情况有多不透明”? 有些非常不透明。 其他的则足以让您可以解析消息字符串并弄清楚要对用户说些什么。
The exceptions thrown by Hibernate (or any ORM component) tend to be hard to interpret.
If the exception has enough information that you can produce an error message that actually helps the user, then just catch the exception, analyze it, and move on.
If the exception doesn't have enough information, then you have to check for the error condition, and produce a helpful error message to the user that they're doing something wrong.
The question is one of "how opaque is the exception"? Some are pretty opaque. Others have enough that you can parse the message string and figure out what to say to the user.
一旦 hibernate 从会话中抛出异常,您必须丢弃会话< /a>(参见第 11.2.3 节)。 因此,如果您需要检查重复并继续使用同一会话,那么您别无选择,只能首先在应用程序中进行检查。
第一个片段中的代码也有可能是另一个进程插入一条记录,这会导致在检查重复记录的时间和实际插入记录的时间之间引发重复异常。
Once hibernate throws an exception from the session you must discard the session (see section 11.2.3). So, if you need to check for dups and continue using the same session then you have no choice but to check first in the application.
Also there is a possibility with the code in the 1st snippet that another process could insert a record that would cause the duplicate exception to be thrown between the time you check for the duplicate record and the time it actually gets inserted.