我正在使用带有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吗?
答案 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()
提交交易,这在几个方面都是错误的:
em.getTransaction().commit()