逻辑:数据库或者应用程序/ 2(约束检查)
这是此问题的特定版本。
我想检查是否要插入重复的行。我应该在我的应用程序层中以编程方式检查它吗:
if (exists(obj)) { throw new DuplicateObjectException(); } HibernateSessionFactory.getSession().save(obj);
还是应该捕获数据库层引发并在违反约束时触发的异常?
try { HibernateSessionFactory.getSession().save(obj); } catch(ConstraintViolationException e) { throw new DuplicateObjectException(); }
编辑:换句话说:尽管约束仍然存在(无论如何它都是很好的数据库设计,而且我不能确定我的应用程序将是唯一访问表的应用程序)我应该依靠约束并处理其违规的异常会加薪,还是我还是最好检查一下?
EDIT2:当然,我会在事务中执行检查+插入操作,锁定表以确保在此期间没有其他进程在写另一条记录
解决方案
通常,我会尽量避免依赖于抛出的错误进行编码,因为我做错了什么。但是有时候,这就是我们所能做的。根据情况,我认为我们应该先检查一下。
我们需要捕获数据库异常,除非可以保证应用程序是唯一在数据库中插入行(并且将插入行)的应用程序。
编辑:我可能有一个误解的问题,但我仍然会争论选项B(HibernateSessionFactory从数据库中抛出ConstraintException)是更好的选择。在检查和实际的函数调用之间的某个时间段内,另一个应用程序总是很有可能会插入一些内容。此外,检查重复项的唯一方法是执行添加查询,这只是不必要的性能消耗。
我对该问题的最初理解是,在选项A中将在内部执行重复检查(即,仅使用程序已创建的数据结构,并且直到INSERT才进行查询)。我最初的回答是对这种方法的回应。
首先,我们必须对数据库具有主键或者唯一性约束才能正确地强制执行此唯一性。
鉴于存在约束,我们应该在应用程序中以哪种方式编码?我的偏好是尝试插入并捕获异常。因为大概大多数插入操作都会成功,所以只有少数插入操作会失败(这就是"异常"的含义!):当数据库将要执行其自身的约束检查时,在每个插入操作之前执行存在检查效率很低。
同样,如果其他人设法在存在检查和插入之间的小间隔内提交具有相同键值的记录,则存在检查在理论上还是有可能出错。然后,如果我们不捕获数据库异常,则我们会相信插入实际上是成功的,但实际上并没有成功。
如果由于某种原因(通常是DBA忽略重新启用它的维护工作)而取消约束,这将中断(允许重复输入)。我们应该在应用程序中检查这种情况。
但是,最好让数据库强制执行约束(如我们所正确指出的那样),因为其他人也可能正在使用该数据库。概括地说,最好假设应用程序和数据库处于M:M关系中,几乎所有时间都是这种情况。
Hibernate(或者任何ORM组件)抛出的异常往往难以解释。
如果异常具有足够的信息,我们可以产生一条实际上对用户有帮助的错误消息,则只需捕获该异常,对其进行分析,然后继续进行。
如果异常没有足够的信息,则我们必须检查错误情况,并向用户产生有用的错误消息,说明他们在做错事。
问题是"例外情况有多不透明"?有些是非常不透明的。其他人有足够的能力来分析消息字符串并弄清楚对用户说些什么。
休眠从会话引发异常后,我们必须放弃该会话(请参见11.2.3节)。因此,如果我们需要检查重复项并继续使用同一会话,则别无选择,只能先在应用程序中进行检查。
另外,第一个代码段中的代码还有可能另一个进程插入一条记录,该记录将导致在检查重复记录的时间与实际插入记录之间抛出重复异常。
我们检查对象是否仅存在于应用程序代码中,然后一旦确定不存在该对象,则随意保存该对象。但是另一个并发客户端可能会在两行代码之间插入自己的对象。因此,无论如何,我们都会遇到Duplicate异常,只是这一次我们没有捕获它。
我们必须执行save()并捕获异常。否则,我们将与在同一数据库上工作的其他并发客户端发生竞争。