具有线程争用条件和requires_new事务属性的Java EE持久性异常

时间:2012-01-16 14:54:08

标签: java-ee jboss ejb

我有一个附加了REST Web服务的Java EE应用程序。每个REST调用都有自己的无状态bean来操作。这个bean调用一个帮助bean来创建新项目并将它们保存在数据库中。当REST bean返回该项时,稍微操作它,然后将其合并到数据库中。这是一般流程

// Bean A
void someRESTCall()
{
    Item i = beanB.getItem(...);

    // Possible race condition here
    if(i == null)
    {
       i = beanB.buildItem();
       if(i == null)
       {
          // Assume race condition
          i = beanB.getItemForce(...); // This has transactional attribute REQUIRES_NEW
       }
    }

   // Do some stuff to the item and merge it
   i.setColor("blue");
   entityManager.merge(i);

}

// Bean B
Item buildItem(...)
{
   Item i = new Item();
   i.setName(name); // Name is the primary key, cannot be changed.   

   try
   {
       entityManager.persist(i);
   }
   catch(PersistenceException ex)
   {
     // Assume threading issue / race condition
     i = null;
   }

   return i;
}

我们遇到的原始问题是,如果两个休息调用进入竞争状态,您可能在数据库中有重复项(这会导致约束异常,因为主键不会自动生成。)

所以,为了解决这个问题,我将返回null添加到buildMachine。问题是事务回滚,虽然A继续执行,但在回滚后无法正确更新数据库。

为了解决这个问题,我尝试在buildMachine方法中添加REQUIRES_NEW事务属性。当我这样做时,当beanA将它的更改合并回来时,我得到一个约束异常。

那么,我怎样才能解决竞争条件和约束异常?也许更重要的是,为什么合并调用会导致约束异常?它应该重用buildItem返回的现有项数据库行。更改数据库以允许自动生成的主键不是一个选项。

有些人建议删除beanA中对entityManager.merge()的调用,说它是/是还原剂。如果我这样做,A所做的任何更改都不会合并。

1 个答案:

答案 0 :(得分:2)

你有几个问题。

第一个问题:persist()没有插入数据库中。它只会附加一个瞬态实体。插入仅在刷新时执行(在事务提交之前显式地显式地执行à。捕获由持久性引发的异常将无法正常工作

第二个问题:由于A.someRESTCallB.buildItem在同一个事务中运行,如果JPA抛出任何异常,你唯一能做的就是回滚事务并丢弃会话。抛出任何异常后,Hibernate会话处于不稳定状态,并且无法从它抛出的任何异常中恢复。

所以,我要做的是:

  • 从数据库中获取该项目
  • 如果有,请更新它(不需要在此处调用merge:实体已附加)并返回
  • 如果没有,请使用requires_new事务调用方法来创建和更新项目
  • 如果此方法成功,请返回。
  • 如果此方法抛出异常,则返回步骤1.