MychaL
MychaL

Reputation: 999

JPA : how to persist an object with some cascading relations? still a Javax.persistence.PersistenceException...

I have making many research, but i don't find a real answer to my problem.

I would like to create an object which has many relations, and to persist it.

My entities are : reference defined on a reference type, linked to a manual (maybe, could be null). This reference is defined for one or many aircraft and has some translations.

In order to make easy, i put some annotations for cascading and orphelanRemoval.

Here the code.

@Table(name = "T_E_REFERENCE_REF", 
       uniqueConstraints = @UniqueConstraint(columnNames = "REF_IDENTIFIER"))
public class Reference implements java.io.Serializable {

    @Id
    @Column(name = "REF_ID", unique = true, nullable = false)
    @TableGenerator(name="referenceSeqStore", 
        table="T_S_APP_SEQ_STORE_AST", 
        pkColumnName="AST_SEQ_NAME",
        valueColumnName = "AST_SEQ_VALUE",
        pkColumnValue = "T_E_REFERENCE_REF.REF_ID", 
        allocationSize=1)
    @GeneratedValue(strategy=GenerationType.TABLE, generator="referenceSeqStore")                   
    private Integer id;

    @Column(name = "REF_IDENTIFIER", unique = true, nullable = false, length = 50)  
    private String identifier;

    @Column(name = "REF_LINK")  
    private String link;

    @Column(name = "REF_OBSERVATIONS", length = 4000)
    private String observations;

    @ManyToOne
    @JoinColumn(name = "REF_RFT_ID", nullable = false)  
    private ReferenceType referenceType;

    @ManyToOne
    @JoinColumn(name = "REF_MAN_ID")    
    private Manual manual;  

    @OneToMany(fetch = FetchType.LAZY, 
               mappedBy = "reference",
               cascade = { CascadeType.ALL }, orphanRemoval = true)
    private Set<Translation> translations = new HashSet<Translation>(0);

    @ManyToMany(fetch = FetchType.LAZY,
                cascade = { CascadeType.ALL })
    @JoinTable(name = "T_J_REF_AIR_RFA", 
               joinColumns = { @JoinColumn(name = "RFA_REF_ID", nullable = false, updatable = false) },
               inverseJoinColumns = { @JoinColumn(name = "RFA_AIR_ID", nullable = false, updatable = false) })
    private Set<Aircraft> aircrafts = new HashSet<Aircraft>(0);

    @Transient 
    private String localeTranslation;

}


@Table(name = "T_R_AIRCRAFT_AIR", 
       uniqueConstraints = @UniqueConstraint(columnNames = "AIR_NAME"))
public class Aircraft implements java.io.Serializable {

    @Id
    @Column(name = "AIR_ID", unique = true, nullable = false)
    @TableGenerator(name="aircraftSeqStore", 
        table="T_S_APP_SEQ_STORE_AST", 
        pkColumnName="AST_SEQ_NAME",
        valueColumnName = "AST_SEQ_VALUE",
        pkColumnValue = "T_R_AIRCRAFT_AIR.AIR_ID", 
        allocationSize=1)
    @GeneratedValue(strategy=GenerationType.TABLE, 
        generator="aircraftSeqStore")       
    private Integer id;

    @Column(name = "AIR_NAME", unique = true, nullable = false, length = 50)
    private String name;

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "T_J_REF_AIR_RFA", 
               joinColumns = { @JoinColumn(name = "RFA_AIR_ID", nullable = false, updatable = false) }, 
               inverseJoinColumns = { @JoinColumn(name = "RFA_REF_ID", nullable = false, updatable = false) })
    @OrderBy("identifier")
    private Set<Reference> references = new HashSet<Reference>(0);

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "T_J_MAN_AIR_MNA", 
               joinColumns = { @JoinColumn(name = "MNA_AIR_ID", nullable = false, updatable = false) }, 
               inverseJoinColumns = { @JoinColumn(name = "MNA_MAN_ID", nullable = false, updatable = false) })
    @OrderBy("type")
    private Set<Manual> manuals = new HashSet<Manual>(0);

    @OneToMany(fetch = FetchType.LAZY, 
               mappedBy = "aircraft",
               cascade = { CascadeType.REMOVE })
    private Set<UserConfig> userConfigs = new HashSet<UserConfig>(0);

}

@Table(name = "T_R_REFERENCE_TYPE_RFT", 
       uniqueConstraints = @UniqueConstraint(columnNames = "RFT_TYPE"))
public class ReferenceType implements java.io.Serializable {

    @Id
    @Column(name = "RFT_ID", unique = true, nullable = false)
    @TableGenerator(name="referenceTypeSeqStore", 
        table="T_S_APP_SEQ_STORE_AST", 
        pkColumnName="AST_SEQ_NAME",
        valueColumnName = "AST_SEQ_VALUE",
        pkColumnValue = "T_R_REFERENCE_TYPE_RFT.RFT_ID", 
        allocationSize=1)
    @GeneratedValue(strategy=GenerationType.TABLE, generator="referenceTypeSeqStore")                   
    private Integer id;

    @Column(name = "RFT_TYPE", unique = true, nullable = false, length = 50)    
    private String type;

    @OneToMany(fetch = FetchType.LAZY, 
               mappedBy = "referenceType", 
               cascade = { CascadeType.REMOVE })    
    private Set<Reference> references = new HashSet<Reference>(0);

    @OneToMany(fetch = FetchType.LAZY, 
               mappedBy = "referenceType",
               cascade = { CascadeType.REMOVE })    
    private Set<UserConfig> userConfigs = new HashSet<UserConfig>(0);

    @Transient
    private String typeTranslated;
}

@Table(name = "T_R_MANUAL_MAN", 
       uniqueConstraints = @UniqueConstraint(columnNames = "MAN_TYPE"))
public class Manual implements java.io.Serializable {

    @Id
    @Column(name = "MAN_ID", unique = true, nullable = false)
    @TableGenerator(name="manualSeqStore", 
        table="T_S_APP_SEQ_STORE_AST", 
        pkColumnName="AST_SEQ_NAME",
        valueColumnName = "AST_SEQ_VALUE",
        pkColumnValue = "T_R_MANUAL_MAN.MAN_ID", 
        allocationSize=1)
    @GeneratedValue(strategy=GenerationType.TABLE, generator="manualSeqStore")                  
    private Integer id;

    @Column(name = "MAN_TYPE", unique = true, nullable = false, length = 50)
    private String type;

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "T_J_MAN_AIR_MNA", 
               joinColumns = { @JoinColumn(name = "MNA_MAN_ID", nullable = false, updatable = false) }, 
               inverseJoinColumns = { @JoinColumn(name = "MNA_AIR_ID", nullable = false, updatable = false) })  
    private Set<Aircraft> aircrafts = new HashSet<Aircraft>(0);

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "manual")
    private Set<Reference> references = new HashSet<Reference>(0);

}

@Table(name = "T_E_TRANSLATION_TRL")
public class Translation implements java.io.Serializable {

    @Id
    @Column(name = "TRL_ID", unique = true, nullable = false)
    @TableGenerator(name="translationSeqStore", 
        table="T_S_APP_SEQ_STORE_AST", 
        pkColumnName="AST_SEQ_NAME",
        valueColumnName = "AST_SEQ_VALUE",
        pkColumnValue = "T_E_TRANSLATION_TRL.TRL_ID", 
        allocationSize=1)
    @GeneratedValue(strategy=GenerationType.TABLE, 
        generator="translationSeqStore")            
    private Integer id;

    @ManyToOne
    @JoinColumn(name = "TRL_REF_ID", nullable = false)
    private Reference reference;

    @ManyToOne
    @JoinColumn(name = "TRL_LAN_ID", nullable = false)  
    private Language language;

    @Column(name = "TRL_LABEL", nullable = false, length = 4000)    
    private String label;

}

@Table(name = "T_R_LANGUAGE_LAN", 
       uniqueConstraints = @UniqueConstraint(columnNames = "LAN_LANG"))
public class Language implements java.io.Serializable {

    @Id
    @Column(name = "LAN_ID", unique = true, nullable = false)
    @TableGenerator(name="langSeqStore", 
        table="T_S_APP_SEQ_STORE_AST", 
        pkColumnName="AST_SEQ_NAME",
        valueColumnName = "AST_SEQ_VALUE",
        pkColumnValue = "T_R_LANGUAGE_LAN.LAN_ID", 
        allocationSize=1)
    @GeneratedValue(strategy=GenerationType.TABLE, generator="langSeqStore")        
    private Integer id;

    @Column(name = "LAN_LANG", unique = true, nullable = false, length = 2) 
    private String lang;
}

I coded a JUnit test in order to create a new reference with some objects already stored in the database. But my insert method (persist) fail each time with a javax.persistence.PersistenceException : org.hibernate.PersistentObjectException: detached entity passed to persist.

Here my Junit test :

@Test
@Transactional
public void testInsertReference() {

    // Create transient
    Reference reference = new Reference();
    reference.setIdentifier("MYTEST");
    reference.setLink("MYLINK");
    reference.setObservations("MYCOMMENT");

    // get from database
    Manual manual = manualService.getManual("MYMAN");
    ReferenceType refType = referenceTypeService.getReferenceType(1);

    Aircraft aircraft = aircraftService.getAircraft("MYAIR");
    Set<Aircraft> airList = new HashSet<Aircraft>();
    airList.add(aircraft);

    Language langEn = languageService.getLanguage("EN");
    Language langFr = languageService.getLanguage("FR");

    Translation trlEn = new Translation();
    trlEn.setLanguage(langEn);
    trlEn.setLabel("my ref test");

    Translation trlFr = new Translation();
    trlFr.setLanguage(langFr);
    trlFr.setLabel("mon test de ref");

    List<Translation> trlList = new ArrayList<Translation>();
    trlList.add(trlEn);
    trlList.add(trlFr);         

    // Set some relations
    reference.setManual(manual);
    reference.setReferenceType(refType);
    reference.setAircrafts(airList);
    reference.setTranslations(new HashSet<Translation>(trlList));

    // Persist
    reference = referenceService.insertReference(reference);  // which execute : entityManager.persist(entity)
    logger.info(reference.toString());
}

Any idea ?

Thank you


The stack trace :

javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: myprog.test.persistence.entity.Aircraft
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1365)
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1293)
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1299)
    at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:865)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:365)
    at $Proxy39.persist(Unknown Source)
    at myprog.test.persistence.dao.impl.GenericDaoImpl.create(GenericDaoImpl.java:54)
    at myprog.test.service.impl.ReferenceServiceImpl.insertReference(ReferenceServiceImpl.java:61)
    at myprog.test.service.impl.ReferenceServiceImpl$$FastClassByCGLIB$$f01500a8.invoke(<generated>)
    at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:689)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
    at myprog.test.service.impl.ReferenceServiceImpl$$EnhancerByCGLIB$$a99de5b.insertReference(<generated>)
    at myprog.test.service.ReferenceServiceTest.testInsertReference(ReferenceServiceTest.java:147)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:30)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:30)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: myprog.test.persistence.entity.Aircraft
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:141)
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:802)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:795)
    at org.hibernate.engine.spi.EJB3CascadingAction$1.cascade(EJB3CascadingAction.java:52)
    at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:380)
    at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:323)
    at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:208)
    at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:409)
    at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:350)
    at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:326)
    at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:208)
    at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:165)
    at org.hibernate.event.internal.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:448)
    at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:293)
    at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:193)
    at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:136)
    at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:78)
    at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:208)
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:151)
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:78)
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:811)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:786)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:790)
    at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:859)
    ... 49 more

Upvotes: 1

Views: 933

Answers (1)

gersonZaragocin
gersonZaragocin

Reputation: 1202

Once you get the objects ReferenceType and Aircraft from the database those are not anymore managed by the PersistenceManager, that is the meaning of detached object.

If you need to do this you have to reattach the recovered entities in your ReferenceService, you can use this method:

Aircraft a = em.getReference(Aircraft.class, reference.getAircraft().getId());

You have to do this with all the detached entities before calling persist, remember to set them again to your reference object once it is recovered:

reference.setAircraft(a);

Sometimes the problem is also caused because you have bidirectional associations but you don't set both sides of them.

You can find more information about it here:

http://java.dzone.com/articles/saving_detatched_entities

Regards

Upvotes: 1

Related Questions