13 Replies Latest reply on Aug 7, 2009 7:48 PM by dan.j.allen

    How to avoid LazyInitializationException

    johnnyren
      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?
        • 1. Re: How to avoid LazyInitializationException
          pmuir
          • 2. Re: How to avoid LazyInitializationException
            johnnyren
            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
              admin.admin.email.tld

              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
                admin.admin.email.tld

                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.j.allen

                  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.j.allen

                    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.j.allen

                      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
                        pmuir

                        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.j.allen

                          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
                            pmuir

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

                            • 11. Re: How to avoid LazyInitializationException
                              johnnyren

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

                              • 12. Re: How to avoid LazyInitializationException

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

                                • 13. Re: How to avoid LazyInitializationException
                                  dan.j.allen

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