我有一个这样的实体:
class Parent {
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<ChildUpdatedByBatch> childrenUpdatedByBatch;
@OneToOne(mappedBy = "parent", cascade = CascadeType.ALL)
private Child child;
....
}
我有一个批处理作业,该作业最终会保存/更新一个子实体( ChildUpdatedByBatch ),还有一个常规操作来更新该子实体。
问题是,我们使用 parentRepository 更新两个实体,因此批处理作业具有以下内容:
// updating parent entity by adding/ or updating a ChildUpdatedByBatch
parentRepository.save(parent);
常规操作还使用:
// updating parent entity by adding/ or updating the Child entity
parentRepository.save(parent);
但是第二个(常规操作)它抛出ObjectOptimisticLockingFailureException,因为
Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [<package>.ChildEntityUpdatedByBatch#911].
我想知道,是否为每个子实体创建存储库都能解决问题。我的意思是,像这样:
child.save(child) // with child having a reference to out-of-date parent
或者,如果那样也不能解决问题。
答案 0 :(得分:1)
为Child
本身添加存储库是明智的,但是我怀疑这会解决您的问题。
您仍然必须与孩子一起更新父母,因此,如果我正确理解您的情况,您将仍然有两个parentRepository.save(parent);
操作。因此,无论如何您最终都会得到OptimisticLockException
。
我将简单地应用通用程序来处理这种异常,即:
OptimisticLockException
答案 1 :(得分:0)
我有一个类似的问题,也许我可以澄清问题的根源。
MySQL JDBC 驱动程序(和 MySQL 数据库)使用REPEATABLE READ
作为默认事务隔离级别。它比例如Oracle默认的READ COMMITTED
级别受到限制。
所以我认为会发生什么。
X
已打开。Parent
加载了10条children
记录。另一个事务Y
已启动,并从同一children
删除了所有Parent
记录。
REPEATABLE READ
意味着,如果您将在事务Parent
中再次加载X
,则会看到与{{1}类似的10条children
记录}什么也没做。
但是!当您要删除/更新事务Y
中的children
条记录时,Hibernate会这样做,但是delete / update返回受影响的记录数。
这是一件棘手的事情-受影响的记录数将等于零,因为事务X
已删除了所有记录(这就是所谓的 phantom read )。但是Hibernate认为他应该删除10条记录。收到0条已删除的记录后,由于Hibernate将此情况视为异常,因此Hibernate上升Y
。
我通过将MySQL JDBC驱动程序属性中的事务隔离级别更改为ObjectOptimisticLockingFailureException
来解决此问题。
在MySQL中实现READ COMMITTED
隔离级别非常棘手,因此您可以参考本文。对这个话题有很好的解释