添加 Spring 事务时三重关联的持久性

Posted

技术标签:

【中文标题】添加 Spring 事务时三重关联的持久性【英文标题】:Persistence of a triple association when adding Spring transactions 【发布时间】:2013-06-12 13:44:35 【问题描述】:

我有一个使用 JPA (EclipseLink) 和 Spring 框架的 Java EE 应用程序。

在我添加 Spring 事务管理之前,我的持久性类中一切正常。 我有以下实体(对应于数据库表):

项目

@Entity
@Table(name="projet")
public class Projet implements Serializable 
 private static final long serialVersionUID = 1L;

 @Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 @Column(name="id_projet", unique=true, nullable=false)
 private Integer idProjet;

 @Column(name="nom_projet")
 private String nomProjet;

 /** The projet util droits. */
 @OneToMany(mappedBy="projet", cascade=CascadeType.ALL)
 private Set<ProjetUtilDroit> projetUtilDroits;

     public Projet() 
     

    ...

用户

@Entity
@Table(name="utilisateur")
public class Utilisateur implements Serializable 

     /** The Constant serialVersionUID. */
private static final long serialVersionUID = 1L;

 /** The id utilisateur. */
 @Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 @Column(name="id_utilisateur", unique=true, nullable=false)
 private Integer idUtilisateur;

  /** The nom utilisateur. */
 @Column(name="nom_utilisateur", nullable=false, length=50)
 private String nomUtilisateur;

 //bi-directional many-to-one association to ProjetUtilDroit
 /** The projet util droits. */
 @OneToMany(mappedBy="utilisateur", cascade=CascadeType.REMOVE)
 private Set<ProjetUtilDroit> projetUtilDroits;

      ...
  

@Entity
@Table(name="droit")
public class Droit implements Serializable 

  /** The Constant serialVersionUID. */
 private static final long serialVersionUID = 1L;

 /** The id droit. */
 @Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 @Column(name="id_droit", unique=true, nullable=false)
 private Integer idDroit;

 /** The type droit. */
 @Column(name="type_droit", nullable=false, length=10)
 private String typeDroit;

     /**
      * Instantiates a new droit.
      */
     public Droit() 
     
    ...

以及将用户链接到具有特定权限的项目的关联 (ProjectUserRight)

@Entity
@Table(name="projet_util_droit")
public class ProjetUtilDroit implements Serializable 

 /** The Constant serialVersionUID. */
 private static final long serialVersionUID = 1L;

 /** The id. */
 @EmbeddedId
 private ProjetUtilDroitPK id;

 //bi-directional many-to-one association to Droit
     /** The droit. */
 @ManyToOne(cascade=CascadeType.MERGE, CascadeType.REFRESH)
 @JoinColumn(name="id_droit")
 private Droit droit;

 //bi-directional many-to-one association to Projet
 /** The projet. */
 @MapsId("idProjet")
     @ManyToOne(cascade=CascadeType.MERGE, CascadeType.REFRESH)
     @JoinColumn(name="id_projet")
 private Projet projet;

 //bi-directional many-to-one association to Utilisateur
     /** The utilisateur. */
 @MapsId("idUtilisateur")
     @ManyToOne(cascade=CascadeType.MERGE, CascadeType.REFRESH)
 @JoinColumn(name="id_utilisateur")
 private Utilisateur utilisateur;

    ...
    

关联的嵌入ID:

@Embeddable
public class ProjetUtilDroitPK implements Serializable 

 //default serial version id, required for serializable classes.
 /** The Constant serialVersionUID. */
 private static final long serialVersionUID = 1L;

  /** The id projet. */
  @Column(name="id_projet", unique=true, nullable=false)
  private Integer idProjet;

  /** The id utilisateur. */
  @Column(name="id_utilisateur", unique=true, nullable=false)
  private Integer idUtilisateur;

   ...

我用它的权利创建项目的方法:

   public Projet createProject(String name, int idRight, int idUser) 
    Projet project = new Projet();
    project.setNomProjet(name);
    ProjetUtilDroit pud = new ProjetUtilDroit();
    Droit d = rightDao.findById(idRight);
    pud.setDroit(d);
    pud.setProjet(project);
    Utilisateur user = userDao.findById(idUser);
    pud.setUtilisateur(user);
    if(user.getProjetUtilDroits() == null)
        user.setProjetUtilDroits(new HashSet<ProjetUtilDroit>());
    user.getProjetUtilDroits().add(pud);
    Set<ProjetUtilDroit> pudSet = new HashSet<ProjetUtilDroit>();
    pudSet.add(pud);
    project.setProjetUtilDroits(pudSet);
    project = projectDao.create(project);
    return project;

在我在“createProject”方法上方添加注释@Transactionnal 之前,它就像一个魅力(保留项目和相关的用户权限)......

现在我收到此错误: Avertissement:StandardWrapperValve [dispatcher]:PWC1406:Servlet 调度程序的 Servlet.service() 引发异常 java.lang.IllegalStateException:在同步期间,通过未标记为级联 PERSIST 的关系找到了一个新对象: *****项目权**** 用户名:userName 权利:阅读。 在 org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.discoverUnregisteredNewObjects(RepeatableWriteUnitOfWork.java:304) 在 org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.calculateChanges(UnitOfWorkImpl.java:702) 在 org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.writeChanges(RepeatableWriteUnitOfWork.java:433) 在 org.eclipse.persistence.internal.jpa.EntityManagerImpl.flush(EntityManagerImpl.java:780) 在 org.eclipse.persistence.internal.jpa.EJBQueryImpl.performPreQueryFlush(EJBQueryImpl.java:1298) 在 org.eclipse.persistence.internal.jpa.EJBQueryImpl.executeReadQuery(EJBQueryImpl.java:434) 在 org.eclipse.persistence.internal.jpa.EJBQueryImpl.getResultList(EJBQueryImpl.java:742) 在 com.dao.BasicDAO.findAll(BasicDAO.java:92) 在 com.dao.BasicDAO.create(BasicDAO.java:103) 在 com.services.ProjectService.createProject(ProjectService.java:48) 在 com.services.ProjectService$$FastClassByCGLIB$$67c85b9f.invoke() 在 net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:191) 在 org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:689) 在 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150) 在 org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110) 在 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 在 org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622) 在 com.services.ProjectService$$EnhancerByCGLIB$$398fa756.createProject() 在 com.servlet.Test.handleCreateProject(Test.java:31) 在 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 在 java.lang.reflect.Method.invoke(Method.java:601) 在 org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:213) 在 org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:126) 在 org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96) 在 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617) 在 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578) 在 org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80) 在 org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923) 在 org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852) 在 org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882) 在 org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778) 在 javax.servlet.http.HttpServlet.service(HttpServlet.java:668) 在 javax.servlet.http.HttpServlet.service(HttpServlet.java:770) 在 org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1542) 在 org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:281) 在 org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175) 在 org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:655) 在 org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595) 在 org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:161) 在 org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:331) 在 org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:231) 在 com.sun.enterprise.v3.services.impl.ContainerMapper$AdapterCallable.call(ContainerMapper.java:317) 在 com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:195) 在 com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:849) 在 com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:746) 在 com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1045) 在 com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:228) 在 com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137) 在 com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104) 在 com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90) 在 com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79) 在 com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54) 在 com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59) 在 com.sun.grizzly.ContextTask.run(ContextTask.java:71) 在 com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532) 在 com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513) 在 java.lang.Thread.run(Thread.java:722)

我想的唯一解决方案是在一个事务中创建项目并在另一个事务中单独保存其权利。这是唯一的解决方案还是有人有其他建议?

【问题讨论】:

【参考方案1】:

哪个对象没有被持久化?包括完整的例外。

您需要将与对象的关系标记为级联持久化,或者在持久化项目之前对对象调用persist。

【讨论】:

对象“ProjetUtilDroit”未持久化。我在我的问题中编辑了完整的堆栈跟踪,因为它不适合 cmets...

以上是关于添加 Spring 事务时三重关联的持久性的主要内容,如果未能解决你的问题,请参考以下文章

Spring_7-Spring事务

spring学习笔记声明式事务

Spring事务

Spring事务_1

Spring的事务管理

Spring事务