我有这样一个实体:
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),以及更新Child实体的常规操作。
问题是我们使用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
或者,如果这不能解决问题。
当然,建议为Child
本身添加存储库,但我怀疑它会解决您的问题。
您仍然需要使用子级更新父级,因此如果我正确理解您的情况,您仍然会有两个parentRepository.save(parent);
操作。因此,无论如何你最终会得到一个OptimisticLockException
。
我只是应用处理这种异常的一般程序,即:
OptimisticLockException
我认为,我有一个类似的问题,而且,我可能会澄清问题的根源。
MySQL JDBC驱动程序(和MySQL数据库)使用REPEATABLE READ
作为默认事务隔离级别。它比例如Oracle默认的READ COMMITTED
级别更受限制。
那么在我看来会发生什么。
X
开放。Parent
有10个children
记录。Y
开始并删除了来自同一children
的所有Parent
记录。REPEATABLE READ
意味着,如果你将在事务Parent
中再次加载X
,你会看到像children
一样的10张Y
记录什么也没做。children
中删除/更新X
记录时,Hibernate会这样做,但删除/更新会返回受影响记录的数量。所以这是一个棘手的事情 - 受影响的记录数将等于零,因为事务Y
已经删除了所有记录(这就是所谓的幻像读取)。但是Hibernate认为他应该删除10条记录。在收到0个删除的记录后,Hibernate提升了ObjectOptimisticLockingFailureException
,因为Hibernate认为这种情况是异常的。
我通过在MySQL JDBC驱动程序的属性中将事务隔离级别更改为READ COMMITTED
来解决此问题。
在MySQL中实现REPEATABLE READ
隔离级别非常棘手,因此您可以参考这篇文章。它对这个主题有很好的解释