JPA级联保存异常:org.hibernate.PersistentObjectException: detached entity passed to persist...

数据结构是这样的,一个 Result 带 两个 表,然后两个表又都保存 product

@Data
public class AddScanRecordResult {

    private  FBAReceiveScan  scanRecord;
    private  FBAReceiveBasket  basket;

}
@Entity
@Data
public class FBAReceiveScan extends WMSEntity {

    @ManyToOne
    private Product product;
    private String tracking;
    private String fnsku;
    private int quantity;
    private String note;
    @OneToMany(fetch = FetchType.EAGER)
    private Set<Attachment> attachments;
    @OneToOne(cascade=CascadeType.ALL)
    private FBAReceiveBasket fbaReceiveBasket;
    private String code;
}
@Entity
@Data
public class FBAReceiveBasket extends WMSEntity {


    private String code;
    private int basketNumber;
    @ManyToOne
    private Product product;
    private int quantity;//篮子里产品数量
    private int boxedQuantity;//已经装箱产品的数量
    private int boxCount; //已经打包箱子数量
    @Column(name = "total_count")
    private int totalCount; //该产品收货总数量

}

代码是这样的

   public AddScanRecordResult addScanRecord(AddScanParmsDTO parms) {

        AddScanRecordResult result = new AddScanRecordResult();
        FBAReceiveScan fBAReceiveScan = new FBAReceiveScan();
        FBAReceiveBasket existingProduct = fBAReceiveBasketRepository.getBasketByProductId(parms.getResult().getProduct().getId(), parms.getCode());
        final FBAReceiveBasket fbaReceiveBasket = existingProduct == null ? new FBAReceiveBasket() : existingProduct;
        // set scan record
        fBAReceiveScan.setProduct(parms.getResult().getProduct());
        fBAReceiveScan.setFnsku(parms.getResult().getScanned());
        fBAReceiveScan.setQuantity(parms.getFbaReceiveQTY());
        fBAReceiveScan.setTracking(parms.getResult().getTracking());
        fBAReceiveScan.setCode(parms.getCode());
        fBAReceiveScan.setFbaReceiveBasket(fbaReceiveBasket);
        fBAReceiveScanRepository.saveAndFlush(fBAReceiveScan);
        //  if product in basket , merge them
        Optional.ofNullable(existingProduct).ifPresentOrElse(x -> {
            existingProduct.setBasketNumber(existingProduct.getBasketNumber());
            int total = 0;
            total = fBAReceiveScanRepository.countQTY(parms.getResult().getProduct().getId(), parms.getCode());
            existingProduct.setQuantity(total);
            existingProduct.setCode(parms.getCode());
            existingProduct.setProduct(existingProduct.getProduct());
            existingProduct.setBasketNumber(existingProduct.getBasketNumber());
            fBAReceiveBasketRepository.saveAndFlush(existingProduct);
            result.setBasket(existingProduct);
        }, () -> {
            // else  init  basket record
            fbaReceiveBasket.setBasketNumber(parms.getBasketNum());
            fbaReceiveBasket.setProduct(parms.getResult().getProduct());
            fbaReceiveBasket.setQuantity(parms.getFbaReceiveQTY());
            fbaReceiveBasket.setCode(parms.getCode());
            fBAReceiveBasketRepository.saveAndFlush(fbaReceiveBasket);
            result.setBasket(fbaReceiveBasket);
        });
        //add scan record in FBAReceiveScan
        result.setScanRecord(fBAReceiveScan);
        return result;
    }

报错是这样的

2022-04-13 08:26:57.088 ERROR 22604 --- [nio-8400-exec-8] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.cwf.wms.inbound.fba.FBAReceiveBasket; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.cwf.wms.inbound.fba.FBAReceiveBasket] with root cause

org.hibernate.PersistentObjectException: detached entity passed to persist: com.cwf.wms.inbound.fba.FBAReceiveBasket
	at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:120)
	at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:118)
	at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:784)
	at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:752)
	at org.hibernate.engine.spi.CascadingActions$7.cascade(CascadingActions.java:298)
	at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:510)
	at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:434)
	at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:220)
	at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:153)
	at org.hibernate.event.internal.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:427)
	at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:264)
	at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:193)
	at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:123)
	at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:185)
	at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:128)
	at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:55)
	at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107)
	at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:760)
	at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:746)
	at jdk.internal.reflect.GeneratedMethodAccessor218.invoke(Unknown Source)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:311)
	at com.sun.proxy.$Proxy144.persist(Unknown Source)
	at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:637)
	at org.springframework.data.jpa.repository.support.SimpleJpaRepository.saveAndFlush(SimpleJpaRepository.java:652)
	at jdk.internal.reflect.GeneratedMethodAccessor231.invoke(Unknown Source)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.springframework.data.repository.core.support.RepositoryMethodInvoker$RepositoryFragmentMethodInvoker.lambda$new$0(RepositoryMethodInvoker.java:289)
	at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:137)
	at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:121)
	at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:529)
	at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:285)
	at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:639)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:163)
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:138)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:80)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:174)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215)
	at com.sun.proxy.$Proxy168.saveAndFlush(Unknown Source)
	at com.cwf.wms.inbound.fba.FBAReceiveService.addScanRecord(FBAReceiveService.java:122)
	at com.cwf.wms.inbound.fba.FBAReceiveService$$FastClassBySpringCGLIB$$580b887f.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
	at com.cwf.wms.inbound.fba.FBAReceiveService$$EnhancerBySpringCGLIB$$ffc0e136.addScanRecord(<generated>)
	at com.cwf.wms.inbound.controller.FBAInBoundReceiveController.addToScanReccords(FBAInBoundReceiveController.java:62)
	at jdk.internal.reflect.GeneratedMethodAccessor256.invoke(Unknown Source)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:681)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:320)
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:126)
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:90)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:118)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:158)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:155)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:92)
	at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:77)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
	at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
	at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
	at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:540)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:359)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:889)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1735)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.base/java.lang.Thread.run(Thread.java:834)

已经尝试过的方法,在 @ManyToOne 上加了 casecade=ALL

也修改了保存的位置,但都没有解决,弄了几天了

您给出的两个表都没有我所认知的id主键。出现这个异常的原因 我认为最大的几率应该是在product ,您这边的业务逻辑两个实体都 setProduct 很有可能导致 product 重复插入相同的数据 因此导致这个问题。

JPA的关联关系很烦,我用JPA都不会按OOP模式建模,所以你这问题我也没遇到过。

网上看了下类似问题,感觉不难。你对比自己业务代码参考参考。

我又改了代码逻辑,分别保存了,就欧克了

谢谢大佬们

纯粹凑个热闹。特别留意了下这个问题。说实话,还原这个问题还挺不容易的,而问题的根源反而不那么复杂。首先,题主的ID,应该不是手动设置的,这样在save的时候默认会为0。这个时候jpa会判断为想要持久化。即 fBAReceiveScan 需要由瞬时态 转换为 持久态。而 FBAReceiveScan 和 FBAReceiveBasket 的级联关系有 persist ,所以传递到 FBAReceiveBasket , 而此时检查到 FBAReceiveBasket 为 游离态,游离态表示数据库已经存在了,是不允许持久化,只能merge。所以导致了这个错误,解决方案也很简单,放在同一个事务中,让 FBAReceiveBasket 保持持久态就行了。如果不想让persist传递,取消其级联即可。

1 Like

专业。 :+1:

好奇心在加上比较感兴趣 :smile:

666