Spring / Hibernate - 删除实体时抛出 StaleStateException

Posted

技术标签:

【中文标题】Spring / Hibernate - 删除实体时抛出 StaleStateException【英文标题】:Spring / Hibernate - StaleStateException thrown when deleting entity 【发布时间】:2021-06-09 07:43:31 【问题描述】:

我的一个实体在尝试删除它时抛出了 StaleStateException。有问题的代码:

@RestController
@RequestMapping("/cycles")
public class DeleteCycleController 
  @DeleteMapping("cycleId")
  @ResponseStatus(HttpStatus.NO_CONTENT)
  public void deleteCycleById(@PathVariable("cycleId") UUID cycleId, Principal principal) 
// cycleRepository extends CrudRepository
    cycleRepository.deleteById(new CycleId(cycleId));
//    deleter.deleteCycle(new CycleId(cycleId));
  


抛出以下内容:

org.springframework.orm.ObjectOptimisticLockingFailureException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; statement executed: HikariProxyPreparedStatement@716092925 wrapping delete from cycle where id='c4c1428e-c296-4199-85f6-8ef16e6999c9'::uuid; nested exception is org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; statement executed: HikariProxyPreparedStatement@716092925 wrapping delete from cycle where id='c4c1428e-c296-4199-85f6-8ef16e6999c9'::uuid

以下是失败前的休眠日志:

2021-03-11 20:34:11.532 DEBUG 55483 --- [nio-8080-exec-7] org.hibernate.SQL                        : requestId=b53dd96089f748cf942fc46867d32fb5, requestMethod=DELETE, correlationId=b53dd96089f748cf942fc46867d32fb5, requestURI=/cycles/c4c1428e-c296-4199-85f6-8ef16e6999c9, userId=5ae11e82-f8df-4fad-ae10-1aa57423ba31, orgId=c7389774-9afa-452b-aa1c-74e7b18bc04d : select cycle0_.id as id1_0_0_, cycle0_.attributes as attribut2_0_0_, cycle0_.created_at as created_3_0_0_, cycle0_.cycle_type_id as cycle_ty4_0_0_, cycle0_.description as descript5_0_0_, cycle0_.end_time as end_time6_0_0_, cycle0_.start_time as start_ti7_0_0_, cycle0_.end_location as end_loca8_0_0_, cycle0_.last_modified_at as last_mod9_0_0_, cycle0_.name as name10_0_0_, cycle0_.organization_id as organiz11_0_0_, cycle0_.start_location as start_l12_0_0_ from cycle cycle0_ where cycle0_.id=?
Hibernate: select cycle0_.id as id1_0_0_, cycle0_.attributes as attribut2_0_0_, cycle0_.created_at as created_3_0_0_, cycle0_.cycle_type_id as cycle_ty4_0_0_, cycle0_.description as descript5_0_0_, cycle0_.end_time as end_time6_0_0_, cycle0_.start_time as start_ti7_0_0_, cycle0_.end_location as end_loca8_0_0_, cycle0_.last_modified_at as last_mod9_0_0_, cycle0_.name as name10_0_0_, cycle0_.organization_id as organiz11_0_0_, cycle0_.start_location as start_l12_0_0_ from cycle cycle0_ where cycle0_.id=?
2021-03-11 20:34:11.533 TRACE 55483 --- [nio-8080-exec-7] o.h.type.descriptor.sql.BasicBinder      : requestId=b53dd96089f748cf942fc46867d32fb5, requestMethod=DELETE, correlationId=b53dd96089f748cf942fc46867d32fb5, requestURI=/cycles/c4c1428e-c296-4199-85f6-8ef16e6999c9, userId=5ae11e82-f8df-4fad-ae10-1aa57423ba31, orgId=c7389774-9afa-452b-aa1c-74e7b18bc04d : binding parameter [1] as [OTHER] - [c4c1428e-c296-4199-85f6-8ef16e6999c9]
2021-03-11 20:34:11.542 DEBUG 55483 --- [nio-8080-exec-7] org.hibernate.SQL                        : requestId=b53dd96089f748cf942fc46867d32fb5, requestMethod=DELETE, correlationId=b53dd96089f748cf942fc46867d32fb5, requestURI=/cycles/c4c1428e-c296-4199-85f6-8ef16e6999c9, userId=5ae11e82-f8df-4fad-ae10-1aa57423ba31, orgId=c7389774-9afa-452b-aa1c-74e7b18bc04d : delete from cycle where id=?
Hibernate: delete from cycle where id=?
2021-03-11 20:34:11.542 TRACE 55483 --- [nio-8080-exec-7] o.h.type.descriptor.sql.BasicBinder      : requestId=b53dd96089f748cf942fc46867d32fb5, requestMethod=DELETE, correlationId=b53dd96089f748cf942fc46867d32fb5, requestURI=/cycles/c4c1428e-c296-4199-85f6-8ef16e6999c9, userId=5ae11e82-f8df-4fad-ae10-1aa57423ba31, orgId=c7389774-9afa-452b-aa1c-74e7b18bc04d : binding parameter [1] as [OTHER] - [c4c1428e-c296-4199-85f6-8ef16e6999c9]

还有 Cycle 实体:

@javax.persistence.Entity
@Accessors(fluent = true)
@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Builder
@TypeDefs(@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class))
public class Cycle 
  @Id private UUID id;
  private CycleName name;
  private CycleDescription description;
  private UUID cycleTypeId;
  private UUID organizationId;
  private Instant createdAt;
  private Instant lastModifiedAt;

  @Type(type = "jsonb")
  private Map<String, Object> attributes;

  private CycleDuration duration;

  @Column(columnDefinition = "Geometry", nullable = true)
  private Geometry startLocation; 

  private Geometry endLocation;

  @OneToMany(mappedBy = "cycleId", cascade = CascadeType.ALL, orphanRemoval = false)
  @Getter(AccessLevel.PRIVATE)
  @Setter(AccessLevel.PUBLIC)
  private List<CycleEntityMapping> entities = new ArrayList<>();
  
  ...

关于可能出现什么问题的任何想法?另一个(更简单的)实体删除得很好。

不确定它是否是 OneToMany 引起的问题,但我尝试更改级联类型、孤儿删除和获取类型,但没有成功。否则会不会是值对象造成的?请注意,更新实体工作正常 - 它只是在删除时失败。

【问题讨论】:

你传递给 repo 的“new CycleId”是什么? 我们将 UUID 包装在对象中,在内部循环存储库调用 cycleJPARepository.deleteById(cycleId.id()); 其中 cycleId.id() 返回 UUID 异常表示数据库中不存在该记录。记录还在吗?它在哪里起作用,还有 UUID 作为 PK 使用的吗? 经过进一步调查,似乎是由删除前触发器引起的。触发器将删除的行复制到另一个表中。删除触发器可以解决问题。不知道为什么触发器会导致这个问题 刚刚进一步调查 - 结果是触发器将其复制到存档表,但由于错误(return new 而不是return old)而没有从现有表中删除。将其更改为 return old 可以解决问题 【参考方案1】:

在这种情况下,它是由删除前触发器中的错误引起的。触发器中有一个错误导致该行未被删除:

CREATE OR REPLACE FUNCTION trigger_delete_row_backup()
    RETURNS trigger AS
$BODY$
    BEGIN
INSERT INTO deleted_cycle values (OLD.*);
    RETURN NEW;
    -- should actually be RETURN OLD
    END;
$BODY$
   language PLPGSQL;

一旦触发器被修复,问题就解决了。

【讨论】:

以上是关于Spring / Hibernate - 删除实体时抛出 StaleStateException的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Spring Data JPA(Hibernate) 跨映射表过滤关联实体?

Hibernate在超类中定义@Where注解

Spring/Hibernate:保存后是不是必须加载实体才能使用它?

spring+hibernate集成动态修改表结构,网上要对LocalSessionFactory进行小小的修改

使用 JPA 和 Spring 加载选定的 Hibernate 实体

JPA、Spring、Hibernate 加载实体 OneToMany 关联的问题