13 Replies Latest reply on Aug 7, 2009 7:48 PM by Dan Allen

    How to avoid LazyInitializationException

    Johnny Ren Newbie
      I have just read a presentation by Pete Muir. 

      http://www.jbossworld.com/downloads/pdf/wednesday/2-25_NEW_Introduction_to_SEAM_Pete_Muir_JBoss.pdf

      I am puzzled by his statements about Stateful session bean and extended persistence context on page 16.

      // start
      Which PC scope to use?
      ? Transaction scoped & detached objects
      - LazyInitializationException
      - NonUniqueObjectException
      - Less opportunity for caching
      ? An extended persistence context of a SFSB is
      - not available during view rendering (LIE again)
      - very complicated propagation rules
      ? No concept of a conversation

      // end

      My question is:

      Do I have do use A Seam-managed persistence context to avoid LIEs?
        • 2. Re: How to avoid LazyInitializationException
          Johnny Ren Newbie
          in the book "JBOSS SEAM", Michael Yuan wrote in chapter 7, section 7.1 " in addition, we gave EntityMnager and EXTENDED type, which allows it to lazy load more data from the database as needed ...".

          Did I miss anything?

          // begin

          @Stateful
          @Name("manager")
          @Scope (SESSION)
          public class ManagerAction implements Manager {

            @In @Out
            private Person person;

            private List <Person> fans;

            private boolean confirmed = false;

            @PersistenceContext
            private EntityManager em;

            public String sayHello () {
              System.out.println("sayHello called");
              if (person.getName()
                      .matches("^[a-zA-Z.-]+ [a-zA-Z.-]+")
                  || confirmed) {

                em.persist (person);
                confirmed = false;
                find ();
                return "fans";

              } else {
                // confirmed = true;
                return "warning";
              }
            }


          • 3. Re: How to avoid LazyInitializationException
            System Administrator Expert

            nice to hear your voice, Pete.  I viewed the web beans Gaving King lecture from Australia today as well.  Interesting annotations...

            • 4. Re: How to avoid LazyInitializationException
              System Administrator Expert

              johnny ren wrote on Jul 10, 2008 05:12:


              in the book JBOSS SEAM, Michael Yuan wrote in chapter 7, section 7.1 in addition, we gave EntityMnager and EXTENDED type, which allows it to lazy load more data from the database as needed ....

              Did I miss anything?


              // begin
              
              @Stateful
              @Name("manager")
              @Scope (SESSION)
              public class ManagerAction implements Manager {
              
                @In @Out
                private Person person;
              
                private List <Person> fans;
              
                private boolean confirmed = false;
              
                @PersistenceContext
                private EntityManager em;
              
                public String sayHello () {
                  System.out.println("sayHello called");
                  if (person.getName()
                          .matches("^[a-zA-Z.-]+ [a-zA-Z.-]+")
                      || confirmed) {
              
                    em.persist (person);
                    confirmed = false;
                    find ();
                    return "fans";
              
                  } else {
                    // confirmed = true;
                    return "warning";
                  }
                }








              With Seam apps, you should use @In EntityManager (SMPC) as much as possible.  Here is a quote from the Seam ref doc:



              Hibernate users developed the open session in view pattern to work around this problem. In the Hibernate
              community, open session in view was historically even more important because frameworks like Spring use
              transaction-scoped persistence contexts. So rendering the view would cause LazyInitializationExceptions
              when unfetched associations were accessed.
              This pattern is usually implemented as a single transaction which spans the entire request. There are several
              problems with this implementation, the most serious being that we can never be sure that a transaction is successful
              until we commit it—but by the time the open session in view transaction is committed, the view is
              fully rendered, and the rendered response may already have been flushed to the client. How can we notify the
              user that their transaction was unsuccessful?
              Seam solves both the transaction isolation problem and the association fetching problem, while working around
              the problems with open session in view. The solution comes in two parts:
              use an extended persistence context that is scoped to the conversation, instead of to the transaction
              use two transactions per request; the first spans the beginning of the update model values phase until the
              end of the invoke application phase; the second spans the render response phase.

              The example code above is using transaction-scoped PersistenceContext (which is the default).  See section 8.4.1 of JSR220-persistence.


              One key difference b/n using @PersistenceContext vs. @In (SMPC) to inject the EntityManager instance is that SMPC introduces flushModeType MANUAL which is basically an extension of JSR220 (the standard options are AUTO or COMMIT).


              Using MANUAL allows you to flush the persistence context manually when you end the conversation (i.e. in a @End method).  This will prevent premature CRUD operations prior to the ending of a conversation.


              There are other advantages to using SMPC EntityManager that have to do with clustering:



              Seam-managed persistence contexts are extremely efficient in a clustered environment. Seam is able to perform
              an optimization that EJB 3.0 specification does not allow containers to use for container-managed extended
              persistence contexts. Seam supports transparent failover of extended persisence contexts, without the need to
              replicate any persistence context state between nodes. (We hope to fix this oversight in the next revision of the
              EJB spec.)

              Here's a key point regarding when/why to use SMPC:



              If you're using Seam outside of a Java EE 5 environment, you can't rely upon the container to manage the persistence context lifecycle for you. Even if you are in an EE 5 environment, you might have a complex application with many loosly coupled components that collaborate together in the scope of a single conversation, and in this case you might find that propagation of the persistence context between component is tricky and error-prone.  In either case, you'll need to use a managed persistence context (for JPA) or a managed session (for Hibernate) in your components. A Seam-managed persistence context is just a built-in Seam component that manages an instance of EntityManager or Session in the conversation context. You can inject it with @In.
              • 5. Re: How to avoid LazyInitializationException
                Dan Allen Master

                Actually, this is not true. You can use the extended persistence context (PersistenceContextType.EXTENDED) on a SFSB to avoid lazy initialization exceptions in both the view and any subsequent request in the same conversation.


                In a Seam application, a SFSB is stored in the conversation (by default). Since an extended persistence context attaches to the SFSB for its lifetime, the extended persistence context lives in the conversation transitively.


                That raises the question, How does the extended persistence context from Java EE and the Seam-managed persistence context differ, then? You'll be happy to know that this is covered in great detail in chapter 8/9 of Seam in Action. I spent time with it because it is an important question. (There is also great coverage in chapter 11 of Java Persistence w/ Hibernate)


                Here are the downsides of using a SFSB to manage an extended persistence context:



                • The persistence context is bound to a single component (the SFSB) and is closed when that component is removed

                • The rules for propagating an extended persistence context between SFSB are complex, stupid, and arbitrary

                • The persistence context is not easily accessed from JavaBean components (you have to basically hack it)

                • You don't get manual flushing of the persistence context controlled by Seam



                Basically, the extended persistence context in Java EE is a nice first try, but Seam does it sooooo much better. So unless you enjoy using pure Java EE resources for the fun of it, use the Seam-managed persistence context.

                • 6. Re: How to avoid LazyInitializationException
                  Dan Allen Master

                  Because I am a man of my code, I have decided to back this up with something you can test. Create a seam-gen EAR app. Now add the following


                  User.java


                  @Entity
                  public class User implements Serializable {
                       private Long id;
                       private String name;
                       private Set<Email> emails;
                  
                       @Id @GeneratedValue
                       public Long getId() { return id; }
                       public void setId(Long id) { this.id = id; }
                  
                       @Length(max = 20)
                       public String getName() { return name; }
                       public void setName(String name) { this.name = name; }
                  
                       @OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
                       public Set<Email> getEmails() { return emails; }
                       public void setEmails(Set<Email> emails) { this.emails = emails; }
                  }



                  Email.java


                  @Entity
                  public class Email implements Serializable {
                       private Long id;
                       private String name;
                  
                       @Id @GeneratedValue
                       public Long getId() { return id; }
                       public void setId(Long id) { this.id = id; }
                  
                       @Length(max = 20)
                       public String getName() { return name; }
                       public void setName(String name) { this.name = name; }
                  }



                  UserList.java


                  @Local
                  public interface UserList {
                       public List<User> findUsers();
                       public void showEmails(boolean state);
                       public Object getDelegate();
                       public void remove();
                  }



                  UserListBean.java


                  @Stateful
                  @Name("userList")
                  public class UserListBean implements UserList {
                  
                       @PersistenceContext(type = PersistenceContextType.EXTENDED)
                       private EntityManager em;
                       
                       @Out(scope = ScopeType.EVENT)
                       boolean showEmails = false;
                  
                       @Factory("users")
                       @Begin(flushMode = FlushModeType.MANUAL)
                       public List<User> findUsers() {
                            return em.createQuery("select u from User u")
                                 .getResultList();
                       }
                       
                       public void showEmails(boolean state) { showEmails = state; }
                  
                       public Object getDelegate() { return em.getDelegate(); }
                       
                       @Remove     public void remove() {}
                  
                  }



                  users.xhtml


                  <p>Flush mode is #{userList.delegate.flushMode} not MANUAL</p>
                  <rich:dataTable var="_user" value="#{users}">
                      <rich:column>
                          <f:facet name="header">Object Signature</f:facet>
                          #{_user}
                      </rich:column>
                      <rich:column>
                          <f:facet name="header">Name</f:facet>
                          #{_user.name}
                      </rich:column>
                      <rich:column>
                          <f:facet name="header">Emails (lazy loaded)</f:facet>
                          <rich:dataList var="_email" value="#{_user.emails.{e|e}}"
                              rendered="#{showEmails}">
                              #{_email.name}
                          </rich:dataList>
                          <h:outputText value="Emails hidden" rendered="#{not showEmails}"/>
                      </rich:column>
                  </rich:dataTable>
                  <s:link action="#{userList.showEmails(true)}" view="/users.xhtml"
                      rendered="#{not showEmails}" value="Show emails"/>
                  <s:link action="#{userList.showEmails(false)}" view="/users.xhtml"
                      rendered="#{showEmails}" value="Hide emails"/>



                  When the page renders the first time, a long-running conversation is started when the users are retrieved. The email addresses will only be shown when the link is clicked to show them. That proves they are loading super lazily.


                  The message above the table proves that Seam was not able to switch the EntityManager to manual flush mode, even if the provider is Hibernate.


                  Enjoy!

                  • 7. Re: How to avoid LazyInitializationException
                    Dan Allen Master

                    I happen to think my four points are simpler than what the Seam reference documentation says. Frankly, for most people, the clustering argument is lost on them. Besides, until I see some numbers, I'm not going to believe it anyway (I don't just take people's word on these types of things).

                    • 8. Re: How to avoid LazyInitializationException
                      Pete Muir Master

                      Dan Allen wrote on Jul 10, 2008 07:16:



                      • The persistence context is bound to a single component (the SFSB) and is closed when that component is removed

                      • The rules for propagating an extended persistence context between SFSB are complex, stupid, and arbitrary

                      • The persistence context is not easily accessed from JavaBean components (you have to basically hack it)

                      • You don't get manual flushing of the persistence context controlled by Seam






                      • The XPC isn't available during the render phase




                      I happen to think my four points are simpler than what the Seam reference documentation says.

                      So fix it :p

                      • 9. Re: How to avoid LazyInitializationException
                        Dan Allen Master

                        So fix it :p

                        Absolutely. I will be contributing back to the Seam reference documentation just as soon as my book is done at the end of this month ;) I didn't mean to sound so high and mighty ;)



                        The XPC isn't available during the render phase

                        I'm willing to say that we are somehow thinking about two different things, but it's definitely available in the render phase. I think my example proves that.


                        The persistence context is just a glorified hash map. The fact that the hash map is bound to a SFSB which is stored in the conversation means that the XPC has to be available anywhere the conversation lives. And since an EntityManager can open a database connection whenever it needs one, then by those two points, it is available and can perform additional lookups in the render phase.

                        • 10. Re: How to avoid LazyInitializationException
                          Pete Muir Master

                          Yes, you are right, this is due to SMT, not SMPC

                          • 11. Re: How to avoid LazyInitializationException
                            Johnny Ren Newbie

                            Thanks all of you for helping. Special thanks to Dan for providing code example.

                            • 12. Re: How to avoid LazyInitializationException
                              wang wei Newbie

                              if use @In EntityManager not use @PersistenceContext
                              how to set (PersistenceContextType.EXTENDED)

                              • 13. Re: How to avoid LazyInitializationException
                                Dan Allen Master

                                The extended persistence context functionality is implicit for a Seam-managed (or Seam-injected) EntityManager. The short answer, you get it for free.