Java HIbernate“StaleStateException:批更新从更新[0]返回意外的行数;实际行数:0;应为1”

Java HIbernate“StaleStateException:批更新从更新[0]返回意外的行数;实际行数:0;应为1”,java,hibernate,jpa,Java,Hibernate,Jpa,当现有用户发出请求时,该方法首先删除最旧的记录,然后再保存新请求。如果使用oracledb时请求的传入速度不太快,那么下面的代码可以正常工作 public Request saveRequest(Request req) { String user = req.getUser(); // Do a NamedQuery on the entity to pull all requests for user List<Reques

当现有用户发出请求时,该方法首先删除最旧的记录,然后再保存新请求。如果使用oracledb时请求的传入速度不太快,那么下面的代码可以正常工作

   public Request saveRequest(Request req)
   {
      String user = req.getUser();
      
      // Do a NamedQuery on the entity to pull all requests for user
      List<Request> requestList = getRequests(user);
      
      LOGGER.info("Request List size is " + requestList.size() + " for " + user);
      
      // Sort the list then delete the first/oldest request
      Comparator<Request> bySessionDate = Comparator.comparing(Request::getDate);
      Collections.sort(requestList, bySessionDate);
         
      LOGGER.info("Deleting request id " + requestList.get(0).getId());

      deleteById(requestList.get(0).getId());

      Request sreq = requestRepository.create(req);
      LOGGER.info("Saved request for user " + sreq.getUserId());
      return sreq;
   }
但是,如果用户以非常快速的方式提交请求,即happy clicker,则相同的代码会生成StaleStateException错误

2021-01-20 00:42:31,307 INFO  [beez.service.RequestManager] (default task-3) Request List size is 250 for Bob
2021-01-20 00:42:31,307 INFO  [beez.service.RequestManager] (default task-3) Deleting request id 55E43DF4D83E4BF5AD73DE47A49B0DA9
2021-01-20 00:42:31,310 INFO  [beez.service.RequestManager] (default task-3) Deleted: 55E43DF4D83E4BF5AD73DE47A49B0DA9
2021-01-20 00:42:31,313 INFO  [beez.service.RequestManager] (default task-3) Saved request for user Bob
2021-01-20 00:42:31,332 INFO  [beez.service.RequestManager] (default task-7) Request List size is 250 for Bob
2021-01-20 00:42:31,332 INFO  [beez.service.RequestManager] (default task-7) Deleting request id 55E43DF4D83E4BF5AD73DE47A49B0DA9
2021-01-20 00:42:31,492 ERROR [org.jboss.as.ejb3.invocation] (default task-7) WFLYEJB0034: EJB Invocation failed on component RequestManager for method public beez.entity.RequestManager service.RequestManager.saveRequest(beez.entity.RequestManager): javax.ejb.EJBTransactionRolledbackException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
.
.
.
Caused by: org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
似乎在下一条记录出现之前,代码还没有时间完成更改,导致代码尝试删除同一条记录两次。除了在这种方法之前改变前端或其他东西,还有什么方法可以解决这个问题吗

我尝试了@Transactional和@Lock选项,但没有成功。在该线程中花费了大量时间,但解决方案要么不起作用,要么不适用:

如果不希望用户快速点击,强烈建议您考虑在前面取消API请求,以跳回到后面的请求/事件

正如您所说的,由于加载到单个会话中的实体和数据库的当前状态之间的不一致,出现了一些问题

这可以通过多种方式解决,例如使用悲观锁定和select for update带来的性能瓶颈、同步方法。。。。等

处理此问题的最简单方法是使用JPA查询进行删除,并在数据库级别进行排序,因此这始终适用于记录表的当前状态

delete from request where id= (select req.id from request req left join user usr on usr.id = req.userId where usr.userId=? order by req.date LIMIT 1)

请根据您的实体设计更正上述查询。

我尝试了您的建议,但当用户提交包含多个请求的单个提交时,也存在相同的问题。代码在20-30毫秒之间的任意位置以单个请求的形式逐个接收每个请求。因此,在下一个请求到来之前,代码还没有完成删除,这也会触发该用户的删除。在我看来,如果我尝试实现锁定或同步,我将为所有用户执行锁定或同步,这将造成巨大的瓶颈,即用户B将不得不等待用户a的进程完成,等等。我觉得这不应该是唯一的问题。您有一个系统可以为多个用户提供服务,这些用户频繁/多次地提出请求,并在数据库中保存/删除这些请求。现在,我看到的唯一解决方案是修改前端,为快乐点击器实现一个去Bouncer,并批量发送所有请求,而不是在服务中单独爆破它们。@stackbacker delete query不应该抛出过时状态,因为我们直接在数据库上运行它,而不是删除特定实体。另一种方法是启动此操作的内部事务并立即提交,但这可以根据外部事务边界进行还原。您完全正确。同样的错误并不存在……我被一个新问题分散了注意力,这个问题与同一个问题有关。我将此标记为解决方案。谢谢感谢@stackbacker,测试尚未创建任何请求的第一次用户的行为,这包括边缘情况,如第一次用户,以及数据库中有一个请求,而您正试图在两个不同的事务中从中删除
delete from request where id= (select req.id from request req left join user usr on usr.id = req.userId where usr.userId=? order by req.date LIMIT 1)