解决Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1的一种方法

使用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()方法。

@Transactional
public 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)));
}

@Transactional
public 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);
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值