Hibernate @ManyToMany 单向关系[重复]

Posted

技术标签:

【中文标题】Hibernate @ManyToMany 单向关系[重复]【英文标题】:Hibernate @ManyToMany unidirectional relationship [duplicate] 【发布时间】:2018-01-21 09:09:47 【问题描述】:

我想在Account(您可以将其视为学生)实体和Clas-s-room 实体之间实现@ManyToMany 单向。很简单,一个教室可以有很多学生,学生可以有很多教室。我希望它是单向的,因为我希望班级知道它只包含什么学生,学生不必知道他们有什么班级。到目前为止我所拥有的:Clas-s-room.java

@Entity(name = "Clas-s-room")
@Table(name = "clas-s-room")
public class Clas-s-room extends BaseEntity

    @Column(name = "clas-s-room_id", nullable = false, length = 20, unique = true)
    private String clas-s-roomId;
    @Column(name = "module_code", nullable = false, length = 50, unique = true)
    private String moduleCode;
    @Column(name = "clas-s-room_name", nullable = false, length = 100, unique = true)
    private String clas-s-roomName;
    @Column(name = "password", nullable = false, length = 45)
    private String password;
    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinTable(name = "clas-s-room_student",
            joinColumns = @JoinColumn(name = "clas-s-room_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;

我通过以下代码(使用 Spring JPA)保存它:

clas-s-room.setClas-s-roomId("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
clas-s-room.setStudents(account);
clas-s-roomService.insertOrUpdate(clas-s-room);

但我收到以下错误:

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

这是堆栈跟踪:

javax.faces.FacesException: #clas-s-roomBean.createClas-s-room(): 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.Clas-s-roomService$$EnhancerBySpringCGLIB$$fd4dbc5d.insertOrUpdate(<generated>)
    at com.peter.uass.bean.Clas-s-roomBean.createClas-s-room(Clas-s-roomBean.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

对此有任何想法吗?我实施的关系有什么问题吗?

【问题讨论】:

可以分享一下accountService的代码吗? @AmerQarabsa accountService 里面什么都没有,accountService 所做的只是调用 Spring 存储库来保存实体。 在这里查看帖子,***.com/questions/19610608/… 【参考方案1】:

如果级联实体没有 ID,则在级联子级上,如果在级联中指定了操作(在您的情况下,您是级联所有操作),它将被级联,但如果实体确实有一个 ID,则意味着实体已被持久化,由于该实体不受管理,因此将被视为分离实体。

在调用服务后,会话将关闭,因此实体将不再处于持久性上下文中,而不是从服务中获取它,而是使用您的存储库获取您的实体。

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



clas-s-room.setClas-s-roomId("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
clas-s-room.setStudents(account);
clas-s-roomService.insertOrUpdate(clas-s-room);

【讨论】:

如果我删除 CascadeType,我得到mysqlNonTransientConnectionException: No operations allowed after statement closed. 错误 解决方案不是删除级联,是的,您将遇到另一个问题,即您正在引用临时实体(未持久化)来持久化实体,直接使用您的存储库而不是服务,这样您就可以将检索到的对象放在同一个会话中

以上是关于Hibernate @ManyToMany 单向关系[重复]的主要内容,如果未能解决你的问题,请参考以下文章

Hibernate 单向多对多更新目标构成关系

Hibernate—— 一对多 和 多对多关联关系映射(xml和注解)总结(转载)

JPQL manyToMany 单向查询

Hibernate学习笔记 --- 创建基于中间关联表的多对多映射关系

Hibernate - 自动 @ManyToMany 更新

使用 Spring JPA 的单向多对多映射