EntityManager and Conversation contex
kruno Apr 8, 2007 10:25 AMI read both books Seam Contextual components and JBoss® Seam: Simplicity and Power Beyond Java? EE from Safari but I can't seem to find the answer.
I was under impression that persistence context is the same as hiberante session and that if @PersistenceContext(type = EXTENDED) that it would last during conversation, and that it would be shared between EntityManagers from two different action beans if they are in the same conversation.
I hope sample code would explain better my question:
I made a little example.
first there are two entity beans directory and file
directory has a set of files
Directory:
package orka.test; import java.util.LinkedHashSet; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.Table; import org.jboss.seam.annotations.Name; @Entity @Name("directory") @Table(name="directory") public class Directory { @Id @GeneratedValue(strategy=GenerationType.TABLE) private long sifra; private String name; @OneToMany(cascade={CascadeType.ALL},mappedBy="directory") private Set<File>files= new LinkedHashSet<File>(); public Set<File> getFiles() { return files; } public void setFiles(Set<File> files) { this.files = files; } public long getSifra() { return sifra; } public void setSifra(long sifra) { this.sifra = sifra; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
File:
package orka.test; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import org.hibernate.validator.NotNull; import org.jboss.seam.annotations.Name; @Entity @Name("file") @Table(name="file") public class File { @Id @GeneratedValue(strategy = GenerationType.TABLE) private long sifra; @NotNull private String name; @ManyToOne @JoinColumn(name="directory") private Directory directory; //geters seters public String getName() { return name; } public void setName(String name) { this.name = name; } public long getSifra() { return sifra; } public void setSifra(long sifra) { this.sifra = sifra; } public Directory getDirectory() { return directory; } public void setDirectory(Directory directory) { this.directory = directory; } }
Then two action beans: DirectoryActionBean and FileActionBean
DirectoryActionBean:
package orka.test; import static javax.persistence.PersistenceContextType.EXTENDED; import javax.ejb.Remove; import javax.ejb.Stateful; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import org.jboss.seam.annotations.Create; import org.jboss.seam.annotations.Destroy; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Out; import org.jboss.seam.log.Log; @Stateful @Name("directoryAction") public class DirectoryActionBean implements DirectoryAction { @PersistenceContext(type = EXTENDED) private EntityManager manager; @SuppressWarnings("unused") @org.jboss.seam.annotations.Logger private Log log; @In(required = false) @Out Directory directory; public void saveDir() { manager.persist(directory); } public String goFiles() { return "files"; } @Remove @Destroy public void destroy() { } @Create public void create() { directory = new Directory(); directory.setName("dirOne"); manager.persist(directory); } }
FileActionBean:
package orka.test; import static javax.persistence.PersistenceContextType.EXTENDED; import javax.ejb.Remove; import javax.ejb.Stateful; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import org.jboss.seam.annotations.Destroy; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Out; import org.jboss.seam.log.Log; @Stateful @Name("fileAction") public class FileActionBean implements FileAction { @PersistenceContext(type = EXTENDED) private EntityManager manager; @SuppressWarnings("unused") @org.jboss.seam.annotations.Logger private Log log; @In @Out Directory directory; public void newFile(){ File file= new File(); file.setName("fileOne"); file.setDirectory(directory); directory.getFiles().add(file); manager.persist(file); } public String backToDir(){ return "dir"; } @Remove @Destroy public void destroy() { } }
and at last part of pages.xml:
<page view-id="/directory.jsp"> <begin-conversation join="true"/> <navigation > <rule if-outcome="files"> <redirect view-id="/file.jsp"> </redirect> </rule> </navigation> </page> <page view-id="/file.jsp"> <navigation > <rule if-outcome="dir"> <redirect view-id="/directory.jsp"> </redirect> </rule> </navigation> </page>
I will not bother you with jsp pages let just say that directory.jsp references only directory entity bean a makes calls only to directoryActionBean it is the same pattern for file.jsp
Now to explain what I do: At first
I create one directory from directoryAction than go to file.jsp and create file, add him in directory and persist file.
After that I return focus to directory.jsp and invoke method save.
Then I get exception
org.hibernate.PersistentObjectException: detached entity passed to persist: orka.test.File
javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: orka.test.File at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:647) at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:218) at org.jboss.ejb3.entity.ExtendedEntityManager.persist(ExtendedEntityManager.java:104) at orka.test.DirectoryActionBean.saveDir(DirectoryActionBean.java:33) 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:585) at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:112) at org.jboss.ejb3.interceptor.InvocationContextImpl.proceed(InvocationContextImpl.java:166) at org.jboss.seam.intercept.EJBInvocationContext.proceed(EJBInvocationContext.java:37) at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:55) at org.jboss.seam.interceptors.BijectionInterceptor.bijectNonreentrantComponent(BijectionInterceptor.java:79) at org.jboss.seam.interceptors.BijectionInterceptor.bijectComponent(BijectionInterceptor.java:58) 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:585) at org.jboss.seam.util.Reflections.invoke(Reflections.java:18) at org.jboss.seam.intercept.Interceptor.aroundInvoke(Interceptor.java:169) at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:64) at org.jboss.seam.interceptors.ManagedEntityIdentityInterceptor.aroundInvoke(ManagedEntityIdentityInterceptor.java:36) 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:585) at org.jboss.seam.util.Reflections.invoke(Reflections.java:18) at org.jboss.seam.intercept.Interceptor.aroundInvoke(Interceptor.java:169) at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:64) at org.jboss.seam.interceptors.OutcomeInterceptor.interceptOutcome(OutcomeInterceptor.java:21) 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:585) at org.jboss.seam.util.Reflections.invoke(Reflections.java:18) at org.jboss.seam.intercept.Interceptor.aroundInvoke(Interceptor.java:169) at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:64) at org.jboss.seam.interceptors.ConversationInterceptor.endOrBeginLongRunningConversation(ConversationInterceptor.java:52) 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:585) at org.jboss.seam.util.Reflections.invoke(Reflections.java:18) at org.jboss.seam.intercept.Interceptor.aroundInvoke(Interceptor.java:169) at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:64) at org.jboss.seam.interceptors.MethodContextInterceptor.aroundInvoke(MethodContextInterceptor.java:27) 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:585) at org.jboss.seam.util.Reflections.invoke(Reflections.java:18) at org.jboss.seam.intercept.Interceptor.aroundInvoke(Interceptor.java:169) at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:64) at org.jboss.seam.intercept.RootInterceptor.createSeamInvocationContext(RootInterceptor.java:144) at org.jboss.seam.intercept.RootInterceptor.invokeInContexts(RootInterceptor.java:129) at org.jboss.seam.intercept.RootInterceptor.invoke(RootInterceptor.java:102) at org.jboss.seam.intercept.SessionBeanInterceptor.aroundInvoke(SessionBeanInterceptor.java:50) 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:585) at org.jboss.ejb3.interceptor.InvocationContextImpl.proceed(InvocationContextImpl.java:118) at org.jboss.ejb3.interceptor.EJB3InterceptorsInterceptor.invoke(EJB3InterceptorsInterceptor.java:63) at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101) at org.jboss.ejb3.entity.ExtendedPersistenceContextPropagationInterceptor.invoke(ExtendedPersistenceContextPropagationInterceptor.java:71) at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101) at org.jboss.ejb3.entity.TransactionScopedEntityManagerInterceptor.invoke(TransactionScopedEntityManagerInterceptor.java:54) at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101) at org.jboss.ejb3.AllowedOperationsInterceptor.invoke(AllowedOperationsInterceptor.java:46) at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101) at org.jboss.aspects.tx.TxPolicy.invokeInOurTx(TxPolicy.java:79) ... 68 more
It works fine if I merge directory before persist, but I don't understand why do I have to merge some thing if it was changed in side long running session and it should not have been detached. Maybe I am doing some thing wrong or I did not understand concept of conversation very well.
Like I sad at the beginning I understood that one session could span many request/response cycles and many action beans as long as they are in one conversation and session is long-running.
To merge some thing before persist in some situations is not a big deal, but I would really like to fully understand concepts behind seam
Please Explain!!!
Thanks in advance Kruno.