【问题标题】:Spring Hibernate StaleObjectStateException due to new HashSet?由于新的HashSet,Spring Hibernate StaleObjectStateException?
【发布时间】:2015-06-22 18:12:03
【问题描述】:

我的代码在很长一段时间内都运行良好,但经过几次重构后,我发现我突然无法再保存 Group 对象了。

我遇到了可怕的Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) 错误。在谷歌搜索之后,我当然找到了this StackOverflow question,但这对我没有任何帮助,因为我没有同时做任何事情。

在经历了我的重构之后,我发现唯一的不同就是这个变化。

之前:

    final Collection<Sample> allByBarcode = sampleService.byBarcode(groupRequest.getSamples(), currentUser);

    if (!allByBarcode.isEmpty()) {
        Group group = new Group();

        group.setName(groupRequest.getName());
        group.setSamples(allByBarcode);
        group.setType(groupRequest.getType());
        group.setOwner(currentUser);

        group = repository.save(group);

        return Optional.ofNullable(group);
    }

重构之后(不记得具体原因)变成了:

    final Collection<Sample> allByBarcode = sampleService.byBarcode(groupRequest.getSamples(), currentUser);

    if (!allByBarcode.isEmpty()) {
        Group group = new Group();

        group.setName(groupRequest.getName());
        group.setSamples(new HashSet<>(allByBarcode));
        group.setType(groupRequest.getType());
        group.setOwner(currentUser);

        group = repository.save(group);

        return Optional.ofNullable(group);
    }

改回原来的后,每次都突然又开始工作了,没有任何错误。

谁能解释这个错误的原因是什么,因为这是使它再次工作的代码的唯一区别?

更新 1:

我尝试了另一种变体:

Group group = new Group();

group.setName(groupRequest.getName());
group.setSamples(new ArrayList<>(allByBarcode));
group.setType(groupRequest.getType());
group.setOwner(currentUser);

group = repository.save(group);

请注意 ArrayList 而不是 HashSet - 出于某种原因,此代码有效

我也尝试过LinkedList,它也有效

有什么想法吗?

更新 2:

堆栈跟踪如下: 我已将其删除以删除大量与 Spring 和 Tomcat 相关的跟踪。

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.orm.ObjectOptimisticLockingFailureException: Object of class [uk.ac.sanger.mig.aker.domain.Group] with identifier [93]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [uk.ac.sanger.mig.aker.domain.Group#93]] with root cause

org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [uk.ac.sanger.mig.aker.domain.Group#93]
        at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:2541)
        at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3285)
        at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:3183)
        at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3525)
        at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:159)
        at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:465)
        at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:351)
        at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:350)
        at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:56)
        at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1222)
        at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:425)
        at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
        at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:177)
        at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:77)
        at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:517)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:757)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:726)
        at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:521)
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:291)
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
        at com.sun.proxy.$Proxy116.createGroup(Unknown Source)
        at uk.ac.sanger.mig.aker.controllers.GroupController.store(GroupController.java:107)

【问题讨论】:

  • 您是如何在 Group 对象中定义您的示例元素的。是集合还是列表。
  • 这是一个集合,但我用 HashSet 初始化它。
  • 如果您发布堆栈跟踪以查看可怕异常的确切位置以及它所指的实体,这将有所帮助。您是否尝试过设置类型为 Set 的 samples 属性?
  • 还要考虑不使用BaseEntity.equals和hashCode中生成的id,stackoverflow.com/questions/1638723/…
  • 指的是Group,明天我会发布堆栈跟踪。我也会试试你的建议,谢谢。

标签: java spring hibernate


【解决方案1】:

在您的新代码中,您将创建一个新的HashSet,它将包含allByBarcode 集合中的所有Sample 元素。

我认为这里提出了问题,因为这些元素没有被会话持久化,因为它们是添加到 new HashSet() 实例的新对象,并且它们并没有真正被会话保存,它解释了 Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) @ 987654329@ 但是 allByBarcode 元素是从您的服务中获取的,因此它们被会话持久化识别

而且我看不出需要设置一个新的 HashSet 实例,会话应该在其中保存(持久)元素(这将引发 Exception,因为您正在尝试使用现有标识符保存元素) 虽然设置已经保存的元素更高效、更安全,所以更好地使用:

   group.setSamples(allByBarcode);

【讨论】:

  • 问题是,如果我做HashSet&lt;Sample&gt; samples = new HashSet&lt;&gt;(allByBarcode); 然后调试以与原始allByBarcode 进行比较,它指向完全相同的对象。 Hibernate 是否也在会话中跟踪实际收集?
  • 事实上allByBarcodenew HashSet&lt;&gt;(allByBarcode) 肯定有相同的对象,但区别在于allByBarcode 元素在会话中持久化,因为它们'取自您的服务,因此指的是会话,它们是保存在数据库中的对象,但new HashSet&lt;&gt;(allByBarcode) 元素与allByBarcode 中的元素相同,但实际上它们是包含allByBarcode 元素的新对象,并指会话它们是未持久化并且需要由会话保存的新对象。
  • Group 类中Samples 的类型是什么,name 也是Group 类的主键,否则这里的主键是什么?
  • 从所有给定的信息中,我想到的都是HashSet 的重复对象或主键问题,因为您声明的所有其他集合都允许它并且只有HashSet没有。并且使用HashSet 并更改对象会导致问题,因为作为主键的id 也会被更改。我认为这是这里的问题。
【解决方案2】:

由于您已经将示例对象初始化为HashSet,您是否可以尝试这样做而不是再次初始化它。

group.getSamples().addAll(allByBarcode);

【讨论】:

  • 使用集合时的一般规则是不要引用它们的具体类,您可以将Group Entity更改为使用List和Set,而不是ArrayList和HashSet。
【解决方案3】:

我认为问题在于您的集合 allByBarcode 中有一些重复的对象,当您将集合包装在不允许重复的 HashSet 中时,这些对象就会消失。之后发生的事情是:您的存储库试图多次保存同一个对象。

【讨论】:

  • 恐怕集合中没有重复项,所以不是问题。
猜你喜欢
  • 2019-06-15
  • 2018-10-04
  • 2013-12-24
  • 2014-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-15
相关资源
最近更新 更多