10 Replies Latest reply on Sep 20, 2010 5:01 AM by zottel

    Why is the entity not saved?

    zottel

      Hi,


      I have an entity that was fetched from the database for display in a rich:dataTable. It is linked to another entity of the same type.


      If I change stuff in the current entity and its linked sibling, the changes to the current entity are saved to the database, but those to the linked entity are not. Why? I thought the linked entity must be managed by the entity manager, too? What do I have to do also update the linked entity?


      Sample Code:




      @Entity
      public class TextModule implements java.io.Serializable
      {
        [...]
        private Integer releaseState;
        private TextModule previousVersion;
        [...incl. getters and setters, of course...]
      }
      
      @Name("textModuleHome")
      @Scope(ScopeType.PAGE)
      public class TextModuleHome extends EntityHome<TextModule>
      {
        [...]
        public String release(TextModule textModule)
        {
          textModule.setReleaseState(ReleaseStateSet.RELEASED);
          if (textModule.getPreviousVersion() != null)
          {
            supersede(textModule.getPreviousVersion());
          }
          return this.update();
        }
      
        public String supersede(TextModule textModule)
        {
          textModule.setReleaseState(ReleaseStateSet.SUPERSEDED);
          return "superseded";
        }
        [...]
      }



      release(textModule) is called from a line of rich:dataTable with the TextModule that is displayed in the current line, using an a4j:commandButton (namespace is a: here, not a4j):




      <rich:panel id="listOrText">
        [...]
        <rich:dataTable id="textModuleList" var="_textModule" value="#{textModuleList.resultList}"
          rendered="#{not empty textModuleList.resultList}">
          [...]
          <rich:column styleClass="action">
            <f:facet name="header">Change State</f:facet>
            <a:commandButton action="#{textModuleHome.release(_textModule)}" value="Release"
              rendered="#{_textModule.releaseState == 1}" reRender="listOrText" />
            [...]
          </rich:column>
          [...]
        </rich:dataTable>
      </rich:panel>



      When I click the Release button, textModule gets changed and saved to the database as it should, but textModule.previousVersion is not saved to the database.


      I thought the linked entities the entity manager fetches will automatically be managed, too? Why aren't those changes saved? What can I do to ensure they are saved?


      Thanks,


      Christian

        • 1. Re: Why is the entity not saved?
          lvdberg

          Hi,


          The EntityHome isn't very good at managing associated Entities because it uses a find-method under the hood. If you also want to manage these objects you have to override parts of the Home-methods to be able to also fetch-load these objects.


          Leo


           

          • 2. Re: Why is the entity not saved?
            zottel

            I just checked, and the linked entity really is NOT managed: getPersistenceContext().contains(textModule) in supersede(textModule) returns false, so no wonder it is not saved.


            But why is it not managed? And how do I make it managed? Do I have to find() it first although I already have it or is there a better way?

            • 3. Re: Why is the entity not saved?
              zottel

              Hi Lwo,


              thanks for the clarification. Remember, though, that this is NOT the instance of an EntityHome object, it actually comes from and EntityQuery Object that produces the list for the rich:dataTable. (I only use the EntityHome because I intended to do it differently first, as you have already seen, too. :-) I could put that code into any other component now as long I replace the call to update() by my own code.)


              But it's probably similar there?


              So how do I make an existing object that was taken from the database managed? Or do I have to throw it away find() it again?

              • 4. Re: Why is the entity not saved?
                lvdberg

                Hi,


                There is a difference between the Query and the Home componenst.


                The Query component permits you to define the HQL, so you can add all fetching of collections you really need. If you define lazy fetching on your entities, Hibernate creates a proxy which contains basic information , but the fields are not loaded. So you can do something like counting the entities in the list, but nothing more. If you have defined a Set or Map it's even more complex, because you try to add or delete something and it is NOT getting saved because of the previous. Luckily the getEjQl method helps you to define the loading you really need. Don't try to solve this by changing realtions to eager loading, because you can create a sort of entiity loading avalanche.


                It's a bit more complex with the Home component. It uses the find method of entityManager to retrieve a specific entity. because you can't define the HQL you can't specify the way the entity is loaded (by the way this would be a great enhancement). However you can override the getInstance etc. to get what you want.


                Normally I don't use the Home component, but make extensive use of Query (especially itsbuilt-in paging works great). For handling of entities i extend a Abstract class which contains basic functions for CRUD operations, without any additional fuzz.


                Leo

                • 5. Re: Why is the entity not saved?
                  zottel

                  Ah, so getEJBQL is where I must look. Thanks a lot, I'll try to go that way.

                  • 6. Re: Why is the entity not saved?
                    zottel

                    Sorry, getEjbql(), of course.

                    • 7. Re: Why is the entity not saved?
                      zottel

                      Hm, new solution, new problems ... :-/


                      I changed the basic query of the EntityQuery class to SELECT textModule FROM TextModule textModule LEFT OUTER JOIN FETCH textModule.previousVersion, but all I'm getting is a NullPointerException from Hibernate (I'm using the classic JPA class, not HibernateEntityQuery):




                      Caused by: java.lang.NullPointerException
                           at org.hibernate.hql.ast.HqlSqlWalker.createFromJoinElement(HqlSqlWalker.java:310)
                           at org.hibernate.hql.antlr.HqlSqlBaseWalker.joinElement(HqlSqlBaseWalker.java:3275)
                           at org.hibernate.hql.antlr.HqlSqlBaseWalker.fromElement(HqlSqlBaseWalker.java:3067)
                           at org.hibernate.hql.antlr.HqlSqlBaseWalker.fromElementList(HqlSqlBaseWalker.java:2945)
                           at org.hibernate.hql.antlr.HqlSqlBaseWalker.fromClause(HqlSqlBaseWalker.java:688)
                           at org.hibernate.hql.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:544)
                           at org.hibernate.hql.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:281)
                           at org.hibernate.hql.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:229)
                           at org.hibernate.hql.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:228)
                           at org.hibernate.hql.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:160)
                           at org.hibernate.hql.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:111)
                           at org.hibernate.engine.query.HQLQueryPlan.<init>(HQLQueryPlan.java:77)
                           at org.hibernate.engine.query.HQLQueryPlan.<init>(HQLQueryPlan.java:56)
                           at org.hibernate.engine.query.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:72)
                           at org.hibernate.impl.AbstractSessionImpl.getHQLQueryPlan(AbstractSessionImpl.java:133)
                           at org.hibernate.impl.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:112)
                           at org.hibernate.impl.SessionImpl.createQuery(SessionImpl.java:1623)
                           at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:92)
                           at org.jboss.seam.persistence.EntityManagerProxy.createQuery(EntityManagerProxy.java:81)
                           at org.jboss.seam.framework.EntityQuery.createQuery(EntityQuery.java:175)
                           at org.jboss.seam.framework.EntityQuery.initResultList(EntityQuery.java:73)
                           at org.jboss.seam.framework.EntityQuery.getResultList(EntityQuery.java:65)
                           at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                           at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
                      ...



                      previousVersion can be null, of course, and als annotated now as @Basic(optional = true). So what am I doing wrong? (Again. :-/ )

                      • 8. Re: Why is the entity not saved?
                        lvdberg

                        Hi,


                        not sure now, but
                        (1) try to change the names of the alias (should work, but you never know) to something like tm.
                        (2) How is you relation annotaed, because in the entity I don't see any many-to-one relation. I assume you have the annotation on the get (?), with a Lazy fetch (?)
                        (3) add a null check in a where clause (although this should be handled by Hibernate correctly).


                        Leo

                        • 9. Re: Why is the entity not saved?
                          zottel

                          Hi Leo,


                          oh man, it was the annotation. BIG BIG thanks for your suggestions!


                          I didn't know I had to annotate a unidirectional 1:1 relation—I always thought that was only required if the entity manager had to know what the corresponding field in the other entity was. Turns out this is not true. :-) So the relation was not annotated at all (apart from @Basic(optional=true)), which led to the error.


                          Now it's working as it should. Thank you, Leo, it would probably have taken a LOT more time to find out all you have told me by myself.

                          • 10. Re: Why is the entity not saved?
                            zottel

                            And as I found out now, I wouldn't have had the problem I started this thread for in the first place if I had annotated my entity correctly (it's not a lazy fetch). Oh my ... X-)