【问题标题】:Running find throws a Detached entity passed to persist exception运行 find 会抛出一个 Detached 实体传递给持久异常
【发布时间】:2015-11-04 23:21:28
【问题描述】:

在尝试通过 REST API 更新资源时,我遇到了使用 Spring 和 Hibernate 时遇到的错误。

我用最少的额外属性简化了案例。

基本模型概述

我正在尝试更新一个名为 Rule 的资源。
Rule 有一个 ThingGroup,它表示一组对象。 Rule还有一组Event,代表规则的激活范围。 在应用程序执行期间,运行必须检查该事物组上的某些参数以触发警报。

休眠引发的错误

我的问题是,在下面的规则服务中使用update 方法时,它会失败并出现此错误。

 org.hibernate.PersistentObjectException: detached entity passed to persist: com.smartobjectsecurity.common.domain.rule.Event
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:276)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:221)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:417)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.jpa.repository.support.LockModeRepositoryPostProcessor$LockModePopulatingMethodIntercceptor.invoke(LockModeRepositoryPostProcessor.java:105)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy117.findOne(Unknown Source)
at com.smartobjectsecurity.common.service.thing.ThingGroupServiceImpl.find(ThingGroupServiceImpl.java:62)
at com.smartobjectsecurity.common.service.thing.ThingGroupServiceImpl.find(ThingGroupServiceImpl.java:1)
at com.smartobjectsecurity.common.service.GenericServiceImpl.find(GenericServiceImpl.java:1)
at com.smartobjectsecurity.common.service.GenericServiceImpl$$FastClassBySpringCGLIB$$daaa7267.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:717)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
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.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:653)
at com.smartobjectsecurity.common.service.thing.ThingGroupServiceImpl$$EnhancerBySpringCGLIB$$aa452fd7.find(<generated>)
at com.smartobjectsecurity.common.service.rule.RuleServiceImpl.update(RuleServiceImpl.java:219)

RuleService 的更新方法

下面是 ruleServiceupdate 方法。
首先,我需要使用 reactGroup 服务更新关联的反应组。

@Transactional
public Rule update(final Rule rule, User user) throws UnknownEntityException, MyBadRequestException {
     final Long id = rule.getId();
     Rule found = null;
     try {
          found = find(id, user);
          found.setEvents(rule.getEvents());
          thingGroupService.find(rule.getThingGroup().getId(), user);
          found.setThingGroup(rule.getThingGroup());
          found = dao.saveAndFlush(found);

     } catch (final UnknownEntityException e) {
          final UnknownEntityException ex = new UnknownEntityException("UnknownEntity/ResourceException.rule", "update_unknown_rule");
          ex.setParameters(new Object[] { id });
          throw ex;
     }

     return found;
}

ThingGroupService 的查找方法

 @Override
 @Transactional(rollbackFor = UnknownEntityException.class )
 public ThingGroup find(final Long id, User user) throws UnknownEntityException {
      logger.debug("-> find, id = " + id);
      final ThingGroup found = getDao().findOne(buildSpecificationForIdAndUser(id, user));
      if (found == null) {
           final UnknownEntityException ex = new UnknownEntityException("UnknownEntity/ResourceException.thingGroup", "unknown_thingGroup");
           ex.setParameters(new Object[] { id });
           throw ex;
      }
      logger.debug("<- find : " + found);
      return found;
 }

这里要求的是 buildSpecificationForIdAndUserbuildSpecificationForUser 方法。
它们用于根据用户的权限构建搜索限制。

 @Transactional
protected Specification<ENTITY> buildSpecificationForIdAndUser(final ID id,final User user){
    return new Specification<ENTITY>() {
        @Override
        public Predicate toPredicate(Root<ENTITY> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
            Expression<Long> entityId = root.get("id");


            Predicate userPredicate = buildSpecificationForUser(user).toPredicate(root, query, builder);

            return builder.and(
                builder.equal(entityId, id),
                userPredicate
            );
        }
    };
}

 @Override
@Transactional
protected Specification<ThingGroup> buildSpecificationForUser(final User user) {
    return new Specification<ThingGroup>() {
        @Override
        public Predicate toPredicate(Root<ThingGroup> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
            Expression<Collection<User>> managersOfThingGroup = root.get("managers");
            Expression<Company> thingGroupCompany = root.get("company");

            Predicate isManager = builder.isMember(user, managersOfThingGroup);
            Predicate isSameCompany = builder.equal(thingGroupCompany,user.getCompany());

            return builder.and(isSameCompany,isManager);

        }
    };
}

错误在哪里

当尝试运行 thingGroupService.find(rule.getThingGroup().getId(), user); 时,Hibernate 突然抛出有关 Event 实体的异常(传递给持久化的分离实体)。
我真的不知道这里出了什么问题。
几天来,我一直在搜索各种论坛,但没有找到问题的原因。

问题

为什么Event 实体在对与事件无关的ThingGroup 资源运行查找后突然与会话分离?

【问题讨论】:

  • 您提供的堆栈跟踪中有一个com.smartobjectsecurity.common.service.GenericServiceImpl#find 方法。您能否编辑问题并提供一些有关其目的的信息?
  • buildSpecificationForIdAndUser 方法中有什么?可以发一下吗?
  • @Apokralipsa GenericServiceImpl#findThingGroupServiceImpl.find 覆盖
  • @DraganBozanovic 我在问题中添加了buildSpecificationForIdAndUser 方法
  • @DraganBozanovic 基本上它过滤搜索查询以限制用户可见性,以防止他访问他不应该看到的东西(其他公司对象,而不是事物经理等)

标签: java spring hibernate exception orm


【解决方案1】:

您不必为已附加的实体调用saveAndFlush,因此应将服务方法更改为:

found = find(id, user);
thingGroupService.find(rule.getThingGroup().getId(), user);
found.setThingGroup(rule.getThingGroup());
found.setEvents(rule.getEvents());

found 实体已经与当前的Session 相关联,因此dirty checking mechanism 会检测到所有更改,如果启用了级联,则会传播子entity state transitions

【讨论】:

  • 它没有回答这个问题。问题是它在执行thingGroupService.find(rule.getThingGroup().getId(), user); 时会引发异常(如问题中所述)。它发生在到达saveAndFlush之前
【解决方案2】:

这就是我要求实现buildSpecification 方法的原因。我想检查是否执行了查询或Sessionget/load 方法通过id 检索实体。

基本上,如果刷新模式为AUTO(默认),Hibernate 有时必须在查询执行之前刷新会话以避免查询过时的数据。 FlushMode.AUTO 的 Javadoc:

Session 有时会在查询执行之前被刷新,以便 确保查询永远不会返回陈旧状态。这是默认设置 冲洗模式。

如果您从托管实体中引用了分离的实体,那么您将收到异常。

解决方法是在查询执行前正确重新附加实体,或者将受影响事务的刷新模式更改为COMMIT

entityManager.setFlushMode(FlushMode.COMMIT)

【讨论】:

  • 感谢您的解释
【解决方案3】:

我已经设法解决了这个问题。
但是我不明白为什么它现在有效,我仍在调查。
我只是颠倒了两行代码,Hibernate 停止抛出分离实体异常。

现在我有:

found.setEvents(rule.getEvents());
thingGroupService.find(rule.getThingGroup().getId(), user);

而不是:

thingGroupService.find(rule.getThingGroup().getId(), user);
found.setEvents(rule.getEvents());

也许 Hibernate 在某些时候会自动刷新,但我不确定它为什么解决了这个问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-07-14
    • 2013-11-05
    • 1970-01-01
    • 2012-11-02
    相关资源
    最近更新 更多