欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

解决Batchupdatereturnedunexpectedrowcountfromupdate[0];actualrowcount:0;expected:1的

时间:2023-07-31

使用JPA时,报错Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1

参考文章:SpringBoot系列教程JPA之delete使用姿势详解

出现问题的场景

在两个线程中的异步方法里,都调用delete方法删除同一条数据,而这条数据在库中是存在的,大概率会出现该异常。

原因

JPA在delete某条数据时,实际上是按照条件先select这条数据,然后再delete这条数据by主键id。即先根据条件find出该条数据对应的主键id,然后deleteById删除该条数据。
源码中,JPA实际上调用SimpleJpaRepository中的deleteById()方法。

@Transactionalpublic void deleteById(ID id) {Assert.notNull(id, ID_MUST_NOT_BE_NULL);delete(findById(id).orElseThrow(() -> new EmptyResultDataAccessException(String.format("No %s entity with id %s exists!", entityInformation.getJavaType(), id), 1)));}@Transactionalpublic void delete(T entity) {Assert.notNull(entity, "The entity must not be null!");em.remove(em.contains(entity) ? entity : em.merge(entity));}

推测出现该问题的原因是两个线程都delete,根据JPA的原理,相当于四条语句,两条查询两条删除。而JPA是用不同的队列存不同的操作,当两个线程均先执行select然后再delete时,第二个delete删除时,原本查出来一条数据,应该删除这条数据,但实际上删除了0条数据,因为该条数据已经被第一个delete删除了,导致预期和实际不匹配,会抛出该异常:Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1。

如下图所示,若是两个线程先select完了,再delete就会报错,如果两个线程按顺序一个先删一个后删,就不会有错误。

本来想在执行delete后,flush一下,但是并没有用。即使flush,如图第一种情况还是回报错。

解决方法

使用原生sql语句执行,不使用jpa就可以了。

@Transactional@Modifying@Query(value = "delete from user where name =:name ",nativeQuery = true)void deleteByName(@Param("name") String name);

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。