8 Replies Latest reply on Apr 9, 2007 5:52 PM by Wayne Bagguley

    Changes not persisted using EntityHome

    Greg Zoller Newbie

      Hello,

      This may sound like a Hibernate issue (it might be), but I'm leaning toward the opinion that its more related to how I'm using Seam/contexts.

      I'm using an entityManager object to handle a DataModel as shown in the Seam tutorial Chapter 1. I have a simple display screen with a delete link next to each line item, which calls #{timecardManager.delete}

      Stripped a bit the manager looks like this:

      @Stateful
      @Scope(ScopeType.SESSION)
      @Name("timecardManager")
      public class TimecardManagerBean implements TimecardManager
      {
       @DataModel
       private List<Timecard> tcList;
      
       @DataModelSelection
       @Out(required=false)
       private Timecard tc;
      
       @In
       private ProjectHome projectHome;
      
       @Factory("tcList")
       public void findTimecards()
       {
       tc = null;
       tcList = projectHome.getInstance().getTimecards();
       }
      
       public void delete() {
       projectHome.getInstance().getTimecards().remove(tc);
       tc = null;
       }
      
       @Remove @Destroy
       public void destroy() {}
      }
      


      A snip of the display code that calls delete looks like this:

      <h:dataTable id="timecardListResults" width="100%" cellspacing="0" cellpadding="0"
       value="#{tcList}" var="tc"
       rendered="#{tcList.rowCount>0}">
       <h:column>
       <h:commandLink
       value="Delete"
       action="#{timecardManager.delete}"
       rendered="#{s:hasRole('ADMIN')}"/>
       </h:column>
      </h:dataTable>
      


      My problem is this... When the users clicks "Delete" #{timecardManager.delete} is successfully called and the doomed item is removed from the in-memory list when the screen is re-rendered. But... no deletions actually make it to the database (Timecard table)! The parent table's (Project) version number is updated in the database however.

      The relationship annotation in Project.java for the timecard property looks like this:
       @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "project")
      


      Why wouldn't my deletion be persisted?

      I have another child of Project, Capex. It also has line-item links but they take the user to an intermediate edit screen (within a conversation), so if a delete button is pressed on this screen nearly identical code in capexManager is called and this time deletions are persisted as desired/expected.

      Thinking that maybe persistence works if you delete from the child EntityHome first, I tried modifying my timecardManager.delete to look like this:
      
       @In(create=true)
       private TimecardHome timecardHome;
      
       public void delete() {
      // projectHome.getInstance().getTimecard().remove(tc);
      
       timecardHome.setId(new Long(tc.getTimecardId()));
       timecardHome.find();
       timecardHome.wire();
       timecardHome.remove();
       tc = null;
       }
      
      


      but this only gave me the following exception (dies in EntityHome.remove() on a flush() call). I've checked, and after the find/wire but before the remove() timecardHome seems healthy: non-null instance, managed, wired.

      (snip)
      Caused by: javax.persistence.EntityNotFoundException: deleted entity passed to persist: [com.paragon.spurs.model.Timecard#<null>]
       at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:625)
       at org.hibernate.ejb.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:299)
       at org.jboss.seam.persistence.EntityManagerProxy.flush(EntityManagerProxy.java:83)
       at org.jboss.seam.framework.EntityHome.remove(EntityHome.java:60)
       at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
       at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
       at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
       at java.lang.reflect.Method.invoke(Unknown Source)
       at org.jboss.seam.util.Reflections.invoke(Reflections.java:20)
       at org.jboss.seam.intercept.RootInvocationContext.proceed(RootInvocationContext.java:31)
       at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:57)
       at org.jboss.seam.interceptors.BijectionInterceptor.aroundInvoke(BijectionInterceptor.java:47)
       at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:69)
       at org.jboss.seam.interceptors.ManagedEntityIdentityInterceptor.aroundInvoke(ManagedEntityIdentityInterceptor.java:37)
       at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:69)
       at org.jboss.seam.interceptors.RollbackInterceptor.aroundInvoke(RollbackInterceptor.java:34)
       at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:69)
       at org.jboss.seam.interceptors.TransactionInterceptor$1.work(TransactionInterceptor.java:32)
       at org.jboss.seam.util.Work.workInTransaction(Work.java:37)
       at org.jboss.seam.interceptors.TransactionInterceptor.aroundInvoke(TransactionInterceptor.java:27)
       at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:69)
       at org.jboss.seam.interceptors.MethodContextInterceptor.aroundInvoke(MethodContextInterceptor.java:27)
       at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:69)
       at org.jboss.seam.intercept.RootInterceptor.invoke(RootInterceptor.java:103)
       at org.jboss.seam.intercept.JavaBeanInterceptor.interceptInvocation(JavaBeanInterceptor.java:151)
       at org.jboss.seam.intercept.JavaBeanInterceptor.invoke(JavaBeanInterceptor.java:87)
       at com.paragon.spurs.model.TimecardHome_$$_javassist_14.remove(TimecardHome_$$_javassist_14.java)
       at com.paragon.spurs.TimecardManagerBean.delete(TimecardManagerBean.java:89)
       at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
       at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
       at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
       at java.lang.reflect.Method.invoke(Unknown Source)
       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:57)
       at org.jboss.seam.interceptors.BijectionInterceptor.aroundInvoke(BijectionInterceptor.java:47)
       at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:69)
       at org.jboss.seam.interceptors.MethodContextInterceptor.aroundInvoke(MethodContextInterceptor.java:27)
       at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:69)
       at org.jboss.seam.intercept.RootInterceptor.invoke(RootInterceptor.java:103)
       at org.jboss.seam.intercept.SessionBeanInterceptor.aroundInvoke(SessionBeanInterceptor.java:53)
       at sun.reflect.GeneratedMethodAccessor91.invoke(Unknown Source)
       at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
       at java.lang.reflect.Method.invoke(Unknown Source)
       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:57)
       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:47)
       at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
       at org.jboss.aspects.tx.TxPolicy.invokeInCallerTx(TxPolicy.java:126)
       ... 84 more
      


      Thanks in advance,
      Greg

        • 1. Re: Changes not persisted using EntityHome
          Greg Zoller Newbie

          Got it! A little Easter present at the last moment. Here's the correct way to handle the delete method for people who may have the same issue I did:

           public void delete() {
           timecardHome.getEntityManager().flush();
           timecardHome.getEntityManager().joinTransaction();
           projectHome.getInstance().getTimecard().remove(tc);
           timecardHome.setId(new Long(tc.getTimecardId()));
           timecardHome.find();
           timecardHome.wire();
           timecardHome.remove();
           tc = null;
           }
          


          • 2. Re: Changes not persisted using EntityHome
            Wayne Bagguley Newbie

            That seems a massive overkill.

            Why aren't you using the persistence context?

            Then you can just do exactly as the tutorial says:

            public void delete()
             {
             messageList.remove(message);
             em.remove(message);
             message=null;
             }
            


            em.remove(message) will remove the 'message' record from the database.

            • 3. Re: Changes not persisted using EntityHome
              Greg Zoller Newbie

              It seems like overkill--and its a bit hard to describe w/o seeing it or the following struggles I've had trying to accomplish this very simple operation.

              In a nutshell if I do it exactly like the tutorial I get an java.lang.UnsupportedOperationException. This is because my list comes from my entity's list, which happens to be a Hibernate PersistentBag. While PersistentBag does implement remove(), whatever AbstractList that happens to be used internally within PersistentBag when my program runs does not, so I get this exception. (If PersistenBag always used a backing List that supported remove() then I think I'd be ok here.)

              Rather than use the Hibernate-generated list directly if I make a copy of this list into an ArrayList then I get exceptions about trying to em.remove() a detached object when I go to delete it. So to fix that I use the entityHome object to find a new (attached) copy but then I get this exception when I remove it: javax.persistence.EntityNotFoundException: deleted entity passed to persist. I have no idea what its complaining about here and started feeling I was beating my head against a wall.

              So my solution seems like overkill but it does indeed work. I wish a simpler solution worked, but so far I haven't found one.

              Greg

              • 4. Re: Changes not persisted using EntityHome
                Wayne Bagguley Newbie

                Either you've missed a step or two, or you are doing something unusual somewhere in your code. You do not need to go to the lengths you are going to just to delete something.

                Do you have an @PersistenceContext annotation anywhere in your code?

                Can you show us the entity bean code?

                • 5. Re: Changes not persisted using EntityHome
                  Greg Zoller Newbie

                  Wayne,

                  I think I've found the answer to the mystery. The solution you pointed out from the tutorial wasn't working for me because java.util.List.remove() wasn't implemented for the backing List in Hibernate's PersistentBag at runtime.

                  I found in the Hibernate docs that collections can wind up being mapped to all kinds of things in Hibernate--apparently not all of them supporting remove(). By adding an @OrderBy annotation to my @OneToMany in my Entity as shown below I can coerce Hibernate to use a List collection that supports remove()--and I wanted the sort anyway:

                   @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "project")
                   @OrderBy("date")
                   public List<Timecard> getTimecard() {
                   return this.timecard;
                   }
                   public void setTimecard(List<Timecard> timecard) {
                   this.timecard = timecard;
                   }
                  


                  This list is what I set my @DataModel to in my entity manager code, so that's why all this Hibernate stuff is relevant.

                  So this is what works for me now:

                  public void delete() {
                   tc = em.merge(tc); // needed or I get exceptions about unattached objects
                   em.remove(tc); // no further issues here after @OrderBy in entity
                   tcList.remove(tc); // clean up in-memory @DataModel list
                   projectHome.getInstance().getTimecard().remove(tc); // remove from in-memory Entity list for response page render
                   tc=null;
                  }
                  


                  Greg

                  • 6. Re: Changes not persisted using EntityHome
                    Wayne Bagguley Newbie

                    I've got a one-to-many relationship in one of my beans and have never had any trouble. This is from my entity bean:

                    List<Request> requests = new ArrayList<Request>();
                    
                    @OneToMany
                     public List<Request> getRequests()
                     {
                     return requests;
                     }
                     public void setRequests(List<Request> requests)
                     {
                     this.requests = requests;
                     }


                    If you are having to perform a merge() then that's because you are not using a seam managed persistence context.

                    I am using an injected persistence context in my session bean:
                    @In
                    EntityManager entityManager


                    This is then configured in components.xml and persistence.xml.

                    I'm still experimenting with the 1-many relationship in terms of how changes are persisted when items are added and removed, I can get it to work quite easily but I'm sure there's a better way than the one I'm using.

                    • 7. Re: Changes not persisted using EntityHome
                      Greg Zoller Newbie

                      Hmm...

                      I'm using this in my entity manager:

                       @PersistenceContext
                       private EntityManager em;
                      


                      Not sure why doing it this way the merge is required but if I don't have it I get attached exceptions. It may be possible the entity became detached or somehow got tangled up in a context someplace (session vs conversation vs ???).

                      I'm sure being a relative Hibernate newbie and a relative Seam newbie isn't helping me. :-)

                      • 8. Re: Changes not persisted using EntityHome
                        Wayne Bagguley Newbie

                        Like I said, you need to use merge() if you are not using a seam managed persistence context.

                        Using @PersistenceContext does not mean that it is a seam managed one.

                        From what I can gather, if you obtain your entity bean in one session bean and then try and update it in another session bean then you have to do a merge() unless you are using a seam managed persistence context. This is because each context is unique to each session bean.

                        I don't understand why you are getting errors with your List with Hibernate though. What version are you using?