-
1. Re: Beans that don't share the same entity manager in same conversation
dhinojosa Jul 30, 2009 8:42 PM (in response to dhinojosa)So it seems that is true, when are injecting into a conversation bean using create = true that bean being injected will not use the same entitymanager.
-
2. Re: Beans that don't share the same entity manager in same conversation
asookazian Jul 30, 2009 10:11 PM (in response to dhinojosa)You're using setter injection in this case. According the to
Dependency Injection in EJB 3
refcard:
Although setter injection might seem like a
little more work, it provides a couple of distinct
advantages. First, it is easier to unit-test by
invoking the public setter method from a testing
framework like JUnit. Second, it is easier to put
initialization code in the setter if you need it.So I'm assuming you're using setter injection for one or both of those reasons (most likely the former).
Anyways, that's an interesting find that I haven't seen discussed elsewhere.
Where/how does the LRC begin?
And are you referring to a different EntityManager interface instance or the PersistenceContext that it manages are different?
A SMPC spans a LRC so any conversation-scoped components involved in that LRC should share the same SMPC as long as it's the same EntityManager tied to the same persistence unit, which should be the case in your example code...
Do you get different behavior if you use property/attribute injection rather than setter injection in both components?
-
3. Re: Beans that don't share the same entity manager in same conversation
joblini Jul 31, 2009 3:36 AM (in response to dhinojosa)Hello Daniel,
How have you determined that a different entity manager is being used?
Regards,
Ingo -
4. Re: Beans that don't share the same entity manager in same conversation
dhinojosa Jul 31, 2009 5:22 AM (in response to dhinojosa)Because I had to use merge()! ;)
-
5. Re: Beans that don't share the same entity manager in same conversation
dhinojosa Jul 31, 2009 5:38 AM (in response to dhinojosa)OK, so maybe not
I have an Office object called office and a User object called manager. Office has a @ManyToOne relationship to User through it's manager member. I am using a SMPC and before the flush that throws the Detached Entity Exception, both objects are in a managed state and I verified it with:
System.out.println("contains manager?" + entityManager.contains(manager)); System.out.println("contains office?" + entityManager.contains(office));
So when I do a flush() that is when the Exception is thrown. Now I am leaning towards an issue with @ManyToOne for some reason I think that Hibernate detaches the User in that relationship somehow during the flush.
-
6. Re: Beans that don't share the same entity manager in same conversation
joblini Jul 31, 2009 5:41 AM (in response to dhinojosa)Have you actually compared the object identifiers in debug to be certain that you are dealing with two instances of entity manager?
-
7. Re: Beans that don't share the same entity manager in same conversation
joblini Jul 31, 2009 5:46 AM (in response to dhinojosa)Are you sure that you are setting both sides of the relationship before flushing?
-
8. Re: Beans that don't share the same entity manager in same conversation
dhinojosa Jul 31, 2009 6:00 AM (in response to dhinojosa)It's a unidirectional relationship.
-
9. Re: Beans that don't share the same entity manager in same conversation
dhinojosa Jul 31, 2009 6:02 AM (in response to dhinojosa)No, but I don't believe I am using the same entitymanager anymore. Since I verified that both my beans were attached before sending it to a flush.
-
10. Re: Beans that don't share the same entity manager in same conversation
dhinojosa Jul 31, 2009 7:04 AM (in response to dhinojosa)Woops! They are using DIFFERENT entity managers!!! WTF?
Ok. So lets say I will update an office, and I have this in my pages.xml
<page view-id="/management/office/update.xhtml" login-required="true"> <restrict>#{s:hasRole('Management')}</restrict> <begin-conversation join="true"/> <param name="id" value="#{loadOfficeFactory.id}" required="true" converterId="javax.faces.Long"/> <navigation from-action="#{officeServiceBean.update}"> <rule if-outcome="success"> <end-conversation/> <redirect view-id="/management/office/view.xhtml"/> </rule> </navigation> </page>
Here is the loadOfficeFactory that is referenced in the param above.
@Name("loadOfficeFactory") @Scope(ScopeType.CONVERSATION) public class LoadOfficeFactory implements OfficeFactory{ private EntityManager entityManager; private Long id; @In public void setEntityManager(EntityManager entityManager) { this.entityManager = entityManager; } public void setId(Long id) { this.id = id; } public Long getId() { return id; } /** * Instantiates an Office object * * @return an office object */ @Factory("office") public Office createOffice() { System.out.println("loading office with entityManager:" + entityManager); return entityManager.find(Office.class, id); } }
Now...in my update.xhtml page I have the following
<h:inputText size="30" label="#{messages.manager}" id="manager" value="#{officeServiceBean.managerName}" required="true"/> <rich:suggestionbox height="200" width="200" suggestionAction="#{userAutoComplete.autocomplete}" var="user" for="manager" fetchValue="#{user.firstName} #{user.lastName}" id="user_suggestion" nothingLabel="No users found"> <h:column> <h:outputText value="#{user.firstName}"/> </h:column> <h:column> <h:outputText value="#{user.lastName}"/> </h:column> </rich:suggestionbox>
I use that inputText field and map it to String property in my officeServiceBean. I use that property to do a lookup in the entity manager to return the user with that first and last name (which is done by another service called findUserByFullNameService. See code below:
@Transactional public String update(Office office) throws SystemException { User manager = null; try { manager = findUserByFullNameService.findUserByFullName(managerName); //find the manager object, I also print out the entity manager in this service } catch (NoResultException nre) { statusMessages.addFromResourceBundleOrDefault( StatusMessage.Severity.ERROR, "managerNotFound", "The manager \'{0}\' has already been entered", managerName); return "failure"; } System.out.println("flushing office with entityManager:" + entityManager); System.out.println("contains manager?" + entityManager.contains(manager)); System.out.println("contains office?" + entityManager.contains(office)); office.setManager(manager); //apply the manager to the office. office.setCreatedDate(calendar); office.setUpdatedDate(calendar); auditItem.setCreatedDate(calendar); auditItem.setUpdatedDate(calendar); auditItem.setDate(calendar); auditItem.setVerb("Created"); auditItem.setUser(loggedInUser); office.addAuditItem(auditItem); entityManager.flush(); //KABLOOEY!!!!! statusMessages.addFromResourceBundleOrDefault( StatusMessage.Severity.INFO, "officeUpdated", "The office \'{0}\' has been updated", office.getName()); return "success"; }
I have verified that when I run this thing that at flush, office initially for the page was loaded by one entityManager, another office loaded, user loaded, and flush done by yet another entityManager. But Seam is supposed guarantee(?) that within a Long-term conversation that everything is loaded from the same entityManager including the pages but it isn't.
The results?
loading office with entityManager:org.jboss.seam.persistence.EntityManagerProxy@f79c23 //I think this is the one on the page loading office with entityManager:org.jboss.seam.persistence.EntityManagerProxy@187748f finding user with entityManager:org.jboss.seam.persistence.EntityManagerProxy@187748f flushing office with entityManager:org.jboss.seam.persistence.EntityManagerProxy@187748f
Now here is what I am doing differently and what may be causing this. I am using the office from the page to do the updating
<h:form id="office_update_form"> <ui:include src="edit.xhtml"/> <h:commandButton id="update_office" value="#{messages.update}" action="#{officeServiceBean.update(office)}"/> //I am calling update through a method on the xhtml page!!!!!! </h:form>
Therefore the page has items loaded using a different entityManager than the one used in the backend and voila! problem found.
-
11. Re: Beans that don't share the same entity manager in same conversation
dhinojosa Jul 31, 2009 7:32 AM (in response to dhinojosa)Solved! Thanks Arbi & Ingo
@Name("loadOfficeFactory") @Scope(ScopeType.CONVERSATION) public class LoadOfficeFactory implements OfficeFactory{ private EntityManager entityManager; private Long id; @In public void setEntityManager(EntityManager entityManager) { this.entityManager = entityManager; } public void setId(Long id) { this.id = id; } public Long getId() { return id; } /** * Instantiates an Office object * * @return an office object */ @Begin //Programmic Conversation Start HERE! Rock and Roll! removed from pages.xml @Factory("office") public Office createOffice() { System.out.println("loading office with entityManager:" + entityManager); return entityManager.find(Office.class, id); } }
-
12. Re: Beans that don't share the same entity manager in same conversation
asookazian Aug 6, 2009 1:27 AM (in response to dhinojosa)That's a declarative promotion to LRC (when the method successfully completes IIRC). Programmatic promotion to LRC is via the Conversation API.
And why did this solve your problem? Is there only one EntityManager instance involved in your code/use-case now?
-
13. Re: Beans that don't share the same entity manager in same conversation
asookazian Aug 6, 2009 1:32 AM (in response to dhinojosa)I still do not understand this thread/problem fully. Why were there more than one instance of EntityManager interface and does this happen to anybody else?
When you called entityManager.flush(), were all involved entities in the transaction currently managed in the PC at that point in time? If not, did you try to merge() them into the PC so that they are managed?