我有一个附加了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所做的任何更改都不会合并。
答案 0 :(得分:2)
你有几个问题。
第一个问题:persist()没有插入数据库中。它只会附加一个瞬态实体。插入仅在刷新时执行(在事务提交之前显式地显式地执行à。捕获由持久性引发的异常将无法正常工作
第二个问题:由于A.someRESTCall
和B.buildItem
在同一个事务中运行,如果JPA抛出任何异常,你唯一能做的就是回滚事务并丢弃会话。抛出任何异常后,Hibernate会话处于不稳定状态,并且无法从它抛出的任何异常中恢复。
所以,我要做的是: