10

我在休眠时遇到了一些脏写问题。我添加了@version 字段,以便我可以查看我是否正在写入过期表。这意味着我现在有很多样板代码

try {
   tryWriteToTable();
} catch (PersistenceExcepton) {  //subclasss of OptimisiticLockException
   try {
      tryWriteToTable();
   } catch (PersistenceExcepton) {
       //dont try again - something seriously wrong
   }
}  

我正在使用 Spring,想知道是否有任何东西可以让我定义这种模式。如果有例外,可以让我重复。除了 Spring 之外,我还可以使用其他任何东西来避免所有这些丑陋的样板代码。

我想要这样的东西

@TryTwice
private void tryWriteToTable() ....


 

谢谢

4

2 回答 2

11

实现目标的最佳方法是使用拦截器来捕获OptimisticLockingException并允许您重试操作。

但是请注意,这仅在您获取最新的实体快照并复制没有旧版本属性的分离状态时才有效。像这样的策略很容易丢失更新。

因此,仅当您尝试保存的实体属性子集只能由您自己的进程而不是任何其他并发事务更新时,才应使用重试策略。

为了简化任务,我创建了db-util开源项目,它也可以在 Maven Central 上找到。它基于 Spring AOP,它提供了一个@Retry注解来标记您希望在获得乐观锁定异常时重试的服务。

于 2014-06-27T20:37:32.533 回答
1

我不认为有什么现成的东西可以解决这个重试问题。对于您手头的当前任务,还有一个更好的模式,而不是尝试重做某事或捕获 PersistenceException 对象。

由于您使用的是 Spring,并且很可能使用Java Transaction Service (JTA)进行事务处理或在 JPA 兼容模式下使用 Hibernate。因此,会话的持久性上下文将在事务提交后被清除。这使您的实体与当前(以及任何)会话分离。

因此,您对给定实体所做的每项更改都不再由会话管理(如果您的实体仍由会话管理,您可以使用session.evict(entity)它手动分离它。您可能想在 Hibernate 文档中阅读有关分离对象的内容:Working with分离的物体

现在您可以重新加载实体所代表的数据库元素的当前状态,只需使用MyEntity currentState = session.get(EntityClass.class, detachedEntity.getId());. 通过检查 currentState 对象的属性,您可以轻松地测试版本号。如果它不同,则数据库中的状态已更改,您可以测试特殊条件并采取相应措施。

示例

例如,我们使用我前段时间实现的东西。我们有一个电子邮件系统,将某些业务报告发送给负责人。创建电子邮件需要 5 分钟或更长时间。创建电子邮件会在午夜之后延迟,以免中断日常操作(创建这些报告的数据库利用率很高)

所以我们的(未经测试,仅用于说明)代码如下所示:

session.beginTransaction();
EmailTask task = getRandomNextTask(session);
session.getTransaction().commit(); //end transaction, task is detached

prepareEmail(); //takes 5minutes or more

session.beginTransaction();
EmailTask currentState = session.get(EmailTask.class, task.getId());
if(currentState.getVersion() == task.getVersion() || currentState.hasError()) {
     currentState.markDone();
     session.getTransaction().commit();
     sendEmail();
} 
else
    trashEmail();

所以你可以看到我们首先得到下一个电子邮件任务并开始计算需要一些时间的电子邮件。然后我们检查任务是否没有改变,或者其他进程是否改变了它,我们检查进程是否真的没有发送电子邮件(因为它导致了错误)。如果是这样,我们将任务标记为已完成并实际发送电子邮件。(我们只是假设我们的系统在更改数据库后但在实际发送电子邮件之前不会崩溃。

于 2013-09-29T14:47:14.787 回答