1

I want to implement a @ManyToMany unidirectional between Account(you can treat it as Student) entity and Classroom entity. Very simple, a classroom can have many Students, and Student can have many classrooms. I want it to be unidirectional because I want class knows it contains what student only, student don't have to know they have what classes. This so so far what I have:
Classroom.java

@Entity(name = "Classroom")
@Table(name = "classroom")
public class Classroom extends BaseEntity
{
    @Column(name = "classroom_id", nullable = false, length = 20, unique = true)
    private String classroomId;
    @Column(name = "module_code", nullable = false, length = 50, unique = true)
    private String moduleCode;
    @Column(name = "classroom_name", nullable = false, length = 100, unique = true)
    private String classroomName;
    @Column(name = "password", nullable = false, length = 45)
    private String password;
    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinTable(name = "classroom_student",
            joinColumns = @JoinColumn(name = "classroom_id"), inverseJoinColumns = @JoinColumn(name = "account_id"))
    private Set<Account> students;
}

Account.java

@Entity(name = "Account")
@Table(name = "account")
public class Account extends BaseEntity
{
    @Column(name = "email", nullable = false, length = 50, unique = true)
    private String email;
    @Column(name = "username", nullable = false, length = 20, unique = true)
    private String username;
    @Column(name = "password", nullable = false, length = 45)
    private String password;
    @Column(name = "sid", nullable = false, length = 20, unique = true)
    private String sid;
    @OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "profile_id", nullable = false)
    private Profile profile;
    @Column(name = "acc_type", nullable = false, length = 1)
    private char accType;
}

and I save it by the following code (with Spring JPA):

classroom.setClassroomId("ABCD123321345");
Set<Account> account = new HashSet<>();
account.add(accountService.findByUsername("abc"));
account.add(accountService.findByUsername("def"));
System.out.println("Size: " + account.size());    //result is 2
classroom.setStudents(account);
classroomService.insertOrUpdate(classroom);

But I got the following error:

PersistentObjectException: detached entity passed to persist: com.peter.uass.model.Account

Here is the stacktrace:

javax.faces.FacesException: #{classroomBean.createClassroom()}: org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.peter.uass.model.Account; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.peter.uass.model.Account
    at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:118)
    at javax.faces.component.UICommand.broadcast(UICommand.java:315)
    at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:790)
    at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1282)
    at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:198)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:658)
    at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1682)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:318)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
    at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:734)
    at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:673)
    at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:174)
    at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:416)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:283)
    at com.sun.enterprise.v3.services.impl.ContainerMapper$HttpHandlerCallable.call(ContainerMapper.java:459)
    at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:167)
    at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:206)
    at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:180)
    at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:235)
    at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:283)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:200)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:132)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:111)
    at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
    at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:536)
    at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:112)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:117)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:56)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:137)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:591)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:571)
    at java.lang.Thread.run(Thread.java:745)
Caused by: javax.faces.el.EvaluationException: org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.peter.uass.model.Account; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.peter.uass.model.Account
    at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:101)
    at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102)
    ... 35 more
Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.peter.uass.model.Account; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.peter.uass.model.Account
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:299)
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:244)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:488)
    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.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:133)
    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.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:57)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
    at com.sun.proxy.$Proxy1321.save(Unknown Source)
    at com.peter.uass.service.BaseServiceImpl.insertOrUpdate(BaseServiceImpl.java:27)
    at com.peter.uass.service.BaseServiceImpl$$FastClassBySpringCGLIB$$dbade7b8.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738)
    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:282)
    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:673)
    at com.peter.uass.service.ClassroomService$$EnhancerBySpringCGLIB$$fd4dbc5d.insertOrUpdate(<generated>)
    at com.peter.uass.bean.ClassroomBean.createClassroom(ClassroomBean.java:61)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at javax.el.ELUtil.invokeMethod(ELUtil.java:332)
    at javax.el.BeanELResolver.invoke(BeanELResolver.java:537)
    at javax.el.CompositeELResolver.invoke(CompositeELResolver.java:256)
    at com.sun.el.parser.AstValue.invoke(AstValue.java:283)
    at com.sun.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:304)
    at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:105)
    at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:87)
    ... 36 more
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: com.peter.uass.model.Account
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:139)
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:797)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:790)
    at org.hibernate.jpa.event.internal.core.JpaPersistEventListener$1.cascade(JpaPersistEventListener.java:97)
    at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:352)
    at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:295)
    at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:161)
    at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:381)
    at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:321)
    at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:298)
    at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:161)
    at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:118)
    at org.hibernate.event.internal.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:460)
    at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:294)
    at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:194)
    at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:125)
    at org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:84)
    at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:206)
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:149)
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:75)
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:807)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:780)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:785)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1181)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:298)
    at com.sun.proxy.$Proxy1315.persist(Unknown Source)
    at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:508)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:504)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:489)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:461)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:56)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
    ... 68 more

any idea on this? any wrong with my implemented relationship?

Jens Schauder
  • 77,657
  • 34
  • 181
  • 348
Newbie
  • 1,584
  • 9
  • 33
  • 72

1 Answers1

1

On cascading children if the cascaded entity does not have an ID it will be cascaded if the operation is specified in the cascade (in your case you are cascade all the operations), but if the entity does have an ID that means the entity is already persisted and since the entity is not managed it will be considered detached entity.

after the call of the service the session will be closed so the entity will be no longer in the persistence context, instead of getting it from the service get your entity using your repository.

Autowired
private AccountDao accountDao;//AccoutDao is the jpa repository for the account



classroom.setClassroomId("ABCD123321345");
Set<Account> account = new HashSet<>();
account.add(accountDao.findByUsername("abc"));
account.add(accountDao.findByUsername("def"));
System.out.println("Size: " + account.size());    //result is 2
classroom.setStudents(account);
classroomService.insertOrUpdate(classroom);
Amer Qarabsa
  • 6,412
  • 3
  • 20
  • 43
  • if I remove the CascadeType, I got `MySQLNonTransientConnectionException: No operations allowed after statement closed.` error – Newbie Aug 13 '17 at 10:18
  • The solution is not to remove the cascade, yes you will have another issue that you are persisting an entity with reference to a transient entity(not persisted), use your repository directly instead of service so you can have your retrieved objects in the same session – Amer Qarabsa Aug 13 '17 at 10:35