无法捕获Hibernate OptimisticLockException

时间:2013-04-02 15:52:02

标签: java hibernate java-ee jpa ejb

我正在使用带有Hibernate的EJB 3。 我有一个无状态会话Bean。该bean中有一个方法deleteItem。

当客户端调用deleteItem方法时,发生删除没有任何问题。 但是如果我试图使用for循环调用deleteItem方法并将该循环的限制设置为5-10次,那么有时删除失败。但并非总是如此。

删除操作实际上从2个表中删除数据。子表和父表。 每个Delete都通过执行刷新操作来提交。

正如我已经提到的那样,如果我逐个执行删除,那么没有问题发生,它只会在我尝试同时运行时发生。我得到的例外是

Caused by: java.sql.BatchUpdateException: Cannot delete or update a parent row: a foreign key
constraint fails (`functionTest/MotherTable`, CONSTRAINT `FKBC6CB0E6D5545EFD` FOREIGN KEY
(`MotherTable_FieldId`) REFERENCES `ChildTable` (`childTableId`))

此处无法进行并发删除操作。并且项目删除与其他项目删除操作无关。因此,如果仍然发生并发,则同时删除多个项目不会有问题。

所以,我做出了一个决定 - “可能是客户端正在访问多线程中的Same Bean实例”。在这种情况下,两个线程将相同的实体管理器状态保持在不同的状态。当另一个尚未完成删除子项时,会尝试刷新持久性上下文。 此时BatchUpdateException Occured。 - 这是我的观察。我不是百分百肯定的。

所以为了克服这种情况,我已经去了乐观锁定。我在母表中创建了版本列。现在我得到了OptimisticLockException。但是我无法捕捉异常。下面是我用来捕获OptimisticLockException的代码。

private boolean deleteItem(Item itemId) {

           Item item= getItem(itemId);
       removeChildTableData(item);
       mEm.remove(item);

    try
            {
       mEm.flush();
    }
    catch (OptimisticLockException  e) 
            {
         try {
            Thread.sleep(1000);
             } 
                     catch (InterruptedException e1) {
            e1.printStackTrace();
         }
       deleteItem(itemId);

        }
    catch(Exception ex)
                 {

        if (ex.getCause() instanceof OptimisticLockException) 
                     {
                       try {
                            Thread.sleep(1000);
                           } catch (InterruptedException x) {
                           }
                     deleteItem(itemId);

                     }


      }

    return true;
   }

所以我的目标是捕获OptimisticLockException并重新执行Delete Operation。 我检查了Exception Class Name,它是EntityNotFound。但是我看到在堆栈跟踪中我得到了OptimisticLockException以及StaleObjectStateException。

那么,有人可以指导我如何捕获这个OptimisticLockException吗?

1 个答案:

答案 0 :(得分:0)

你不应该。也是JB所说的+1。这个例外试图告诉你一些事情。您正在尝试在子项仍引用它时删除外键关系的父行。什么是父母和孩子?良好:

a foreign key constraint fails (`functionTest/MotherTable`, CONSTRAINT `FKBC6CB0E6D5545EFD` FOREIGN KEY (`MotherTable_FieldId`) REFERENCES `ChildTable` (`childTableId`))

所以MotherTable.MotherTableFieldId引用了ChildTable.childTableId。而你正在试图删除一个孩子,而它的母亲仍然指着它。那不行。

我很好奇为什么你会以这种方式拥有这种关系。看起来你的模型看起来像这样:

@Entity
@Table(name="MotherTable")
class Mother {
  @Id
  Long id;
  @ManyToOne
  @JoinColumn(name="MotherTable_FieldId")
  Child child;
}

@Entity
@Table(name="ChildTable"
class Child {
  @Id
  @Column(name="childTableId")
  Long id; 
  @OneToMany(mappedBy="child")
  Set<Mother> mothers;
}

这是奇怪的,因为现在你的孩子可以有很多母亲。也许你想要这个:

@Entity
class Mother {
  @Id
  Long id;
  @OneToMany(mappedBy="mother")
  Set<Child> children;
}

@Entity
class Child {
  @Id
  Long id; 
  @ManyToOne
  @JoinColumn(name="mother_id")
  Mother mother;
}

在这种情况下,您的DAO方法如下所示:

@Transactional
public void deleteFamily(Mother mother) {
  for (Child c: mother.getChildren()) {
    em.remove(c);
  }
  em.remove(mother);
}

你也可以使用级联:

@Entity
class Mother {
  @Id
  Long id;
  @OneToMany(mappedBy="mother", cascading=CascadeType.ALL)
  Set<Child> children;
}

将DAO方法简化为:

@Transactional
public void deleteFamily(Mother mother) {
  em.remove(mother);
}

甚至:

@Entity
class Mother {
  @Id
  Long id;
  @OneToMany(mappedBy="mother", cascading=CascadeType.ALL, orphanRemoval=true)
  Set<Child> children;
}

现在你没有em.remove()个孩子:

@Transactional
public void deleteChild(Child child) {
  Mother m = child.getMother();
  m.getChildren().remove(child);
}

此外,您不应该尝试使用em.flush()提交交易,这在几个方面都是错误的:

  1. 使用em.getTransaction().commit()
  2. 提交交易
  3. 想想你要做的事情:deleteFamily应该在一次交易中发生吗?是?然后以这种方式实现它。删除子项后,请勿尝试进行部分提交。
  4. 让其他人为您管理交易会更方便。只需将方法标记为@Transactional,并让您的JTA框架处理详细信息。
  5. DAO方法甚至不应该尝试进行交易。想一想:您可能希望稍后使用几种DAO方法实现服务。如果他们中的每一个都尝试在单独的事务中提交自己,则toto中的服务调用不能是事务。那很糟。因此,如果您想重用DAO方法,请将事务处理内容拖到它们上面的单独层中。