zanstaszek9
zanstaszek9

Reputation: 119

EntityManager detached entity passed to persist after flush when objects attributes are set earlier

I was writing a feature to insert records from XML file to MySQL database in Spring using EntityManager and JpaContext.
I was trying on two files, one with 50 records, second with duplication of them up to 100 records. In my function, for every 50 records persisted, I call flush(), clear() and print message.
I'm uploading Users and they require to encode the password and properly set a Role attribute that is in a different table as a key.
I have a for loop that gets the User from the list and whole operation with entityManager starts. Firstly, I had that user setting in the same loop.

public void insertInBatch(List<User> usersList) {
        EntityManager entityManager = jpaContext.getEntityManagerByManagedType(User.class);
        setUsersRoleAndEncodePasswords(usersList);

        for(int i = 0; i < usersList.size(); i++){
            User user = usersList.get(i);
            // Setting User attributes
            Role role = roleRepository.findByRole("ROLE_USER");
            user.setRoles(new HashSet<Role>(Arrays.asList(role)));
            user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));

            entityManager.persist(user);

            if(i % 50 == 0 && i > 0){
                entityManager.flush();
                entityManager.clear();
                System.out.println("****\t Loaded " + i + " records from " + usersList.size() + "\t****");
            }
        }
    }

And that code worked for 50 and 100 records files.
As Users are taken from the List, I wrote a function that sets the attributes to make code cleaner and called it before the for loop.

 public void insertInBatch(List<User> usersList) {
        EntityManager entityManager = jpaContext.getEntityManagerByManagedType(User.class);
        setUsersRoleAndEncodePasswords(usersList); // Setting User attributes

        for(int i = 0; i < usersList.size(); i++){
            User user = usersList.get(i);
            entityManager.persist(user);
            if(i % 50 == 0 && i > 0){
                entityManager.flush();
                entityManager.clear();
                System.out.println("****\t Loaded " + i + " records from " + usersList.size() + "\t****");
            }
        }
    }

    private void setUsersRoleAndEncodePasswords(List<User> usersList){
        for (User user : usersList){
            Role role = roleRepository.findByRole("ROLE_USER");
            user.setRoles(new HashSet<Role>(Arrays.asList(role)));
            user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
        }
    }

And this works fine for 50 record file, but does not for 100, throwing an expection right after the message is shown (at 50 records file, message is not shown).

javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: stanislaw.appdemo.user.Role

Why is this, when the code logic is nearly the same, and both works for 50 records? I'm assuming that flush() or clear() have to modify the usersList somehow, but how? Full logs from error.

Hibernate: insert into user_role (user_id, role_id) values (?, ?)
Hibernate: insert into user_role (user_id, role_id) values (?, ?)
****     Loaded 50 records from 100 ****
Hibernate: insert into user (active, email, last_name, name, password) values (?, ?, ?, ?, ?)
2020-02-01 22:50:01.612 DEBUG 12592 --- [nio-8080-exec-8] o.s.web.servlet.view.RedirectView        : View name 'redirect:', model {}
2020-02-01 22:50:01.612 DEBUG 12592 --- [nio-8080-exec-8] o.s.web.servlet.DispatcherServlet        : Completed 302 FOUND
javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: stanislaw.appdemo.user.Role
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:154)
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181)
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:188)
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:732)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:694)
    at org.hibernate.engine.spi.CascadingActions$7.cascade(CascadingActions.java:298)
    at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:492)
    at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:416)
    at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:218)
    at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:525)
    at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:456)
    at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:419)
    at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:218)
    at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:151)
    at org.hibernate.event.internal.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:464)
    at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:298)
    at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:198)
    at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:128)
    at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:192)
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:135)
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:62)
    at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:108)
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:702)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:688)
    at jdk.internal.reflect.GeneratedMethodAccessor90.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.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:368)
    at com.sun.proxy.$Proxy110.persist(Unknown Source)
    at jdk.internal.reflect.GeneratedMethodAccessor90.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:314)
    at com.sun.proxy.$Proxy110.persist(Unknown Source)
    at stanislaw.appdemo.admin.AdminServiceImpl.insertInBatch(AdminServiceImpl.java:70)
    at stanislaw.appdemo.admin.AdminServiceImpl$$FastClassBySpringCGLIB$$158389f3.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:769)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:366)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:99)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
    at stanislaw.appdemo.admin.AdminServiceImpl$$EnhancerBySpringCGLIB$$7bf6db3c.insertInBatch(<generated>)
    at stanislaw.appdemo.admin.AdminPageController.importUsersFromXML(AdminPageController.java:133)
    at stanislaw.appdemo.admin.AdminPageController$$FastClassBySpringCGLIB$$413b934d.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:769)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
    at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:69)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
    at stanislaw.appdemo.admin.AdminPageController$$EnhancerBySpringCGLIB$$deac85ce.importUsersFromXML(<generated>)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    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:190)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
    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:660)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    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.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)
    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:119)
    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:119)
    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:358)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1591)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: stanislaw.appdemo.user.Role
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:127)
    at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:118)
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:726)
    ... 136 more
2020-02-01 22:50:01.618 DEBUG 12592 --- [nio-8080-exec-9] o.s.web.servlet.DispatcherServlet        : GET "/admin/users/1", parameters={}
2020-02-01 22:50:01.619 DEBUG 12592 --- [nio-8080-exec-9] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to stanislaw.appdemo.admin.AdminPageController#showAdminAllUsersPage(int, String, Model)
Hibernate: select user0_.user_id as user_id1_1_, user0_.active as active2_1_, user0_.email as email3_1_, user0_.last_name as last_nam4_1_, user0_.name as name5_1_, user0_.password as password6_1_ from user user0_ limit ?
Hibernate: select count(user0_.user_id) as col_0_0_ from user user0_
Hibernate: select roles0_.user_id as user_id1_2_0_, roles0_.role_id as role_id2_2_0_, role1_.role_id as role_id1_0_1_, role1_.role as role2_0_1_ from user_role roles0_ inner join role role1_ on roles0_.role_id=role1_.role_id where roles0_.user_id=?
Hibernate: select roles0_.user_id as user_id1_2_0_, roles0_.role_id as role_id2_2_0_, role1_.role_id as role_id1_0_1_, role1_.role as role2_0_1_ from user_role roles0_ inner join role role1_ on roles0_.role_id=role1_.role_id where roles0_.user_id=?
Hibernate: select roles0_.user_id as user_id1_2_0_, roles0_.role_id as role_id2_2_0_, role1_.role_id as role_id1_0_1_, role1_.role as role2_0_1_ from user_role roles0_ inner join role role1_ on roles0_.role_id=role1_.role_id where roles0_.user_id=?
2020-02-01 22:50:01.626 DEBUG 12592 --- [nio-8080-exec-9] o.s.w.s.v.ContentNegotiatingViewResolver : Selected 'text/html' given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, application/signed-exchange;v=b3;q=0.9, */*;q=0.8]
2020-02-01 22:50:01.626 DEBUG 12592 --- [nio-8080-exec-9] o.s.web.servlet.view.JstlView            : View name 'admin/users', model {totalPagesNumber=86, currentPageNumber=1, usersList=[stanislaw.appdemo.user.User@915e2d3, stanislaw.appdemo.user.User@3736d882, stanislaw.appdemo.user.User@38d60924], recordCounterStart=0, searchParam=}
2020-02-01 22:50:01.626 DEBUG 12592 --- [nio-8080-exec-9] o.s.web.servlet.view.JstlView            : Forwarding to [/WEB-INF/jsp/admin/users.jsp]
2020-02-01 22:50:01.630 DEBUG 12592 --- [nio-8080-exec-9] o.s.web.servlet.DispatcherServlet        : Completed 200 OK

Upvotes: 0

Views: 1003

Answers (1)

Mikko Maunu
Mikko Maunu

Reputation: 42074

Calling entityManager.clear() will remove all entities from persistence context. This includes Role entities that are already assigned to Userentities

In first version of code Role assigned to User always remains in persistence context until entityManager.persist() is called:

//1) now role is in persistence context            
user.setRoles(new HashSet<Role>(Arrays.asList(role)));
user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
//2) so persisting User with Role is not an issue
entityManager.persist(user);

In second example, Role is actively removed from persistence context:

//1) now we have role in persistenceContext 
EntityManager entityManager = jpaContext.getEntityManagerByManagedType(User.class);
setUsersRoleAndEncodePasswords(usersList); // Setting User attributes

for(int i = 0; i < usersList.size(); i++){
    User user = usersList.get(i);
    //2) for 50 first Users this will succeed, because role is in persistence context
    //but for after that it will fail, because role will be removed in 3)
    entityManager.persist(user);
    if(i % 50 == 0 && i > 0){
        entityManager.flush();
        // 3) after this call persistence context is empty, role will not be there
        entityManager.clear();
        System.out.println("****\t Loaded " + i + " records from " + usersList.size() + "\t****");
    }
}

Upvotes: 1

Related Questions