解决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就会报错,如果两个线程按顺序一个先删一个后删,就不会有错误。
解决Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1的一种方法_第1张图片

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

解决方法

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

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

你可能感兴趣的:(数据库相关,解决问题记录,java,spring,boot,jpa)