1 2 Previous Next 15 Replies Latest reply on Dec 27, 2006 7:06 PM by norman.richards

    Getting LazyInitializationErrors with a SMPC

    smokingapipe

      Ok, now I am totally baffled. I integrated Facelets into my Seam application, and now I have started getting LazyInitializationErrors, even though I'm using a Seam-managed persistence context (SMPC). I thought SMPC was supposed to almost completely eliminate LIEs, but they are back. Any ideas on what to look for that could be causing this?

      Thanks

        • 1. Re: Getting LazyInitializationErrors with a SMPC
          smokingapipe

          Ok, I found out a little bit more. I looked at the docs for SeamExtendedManagedPersistencePhaseListener. It says "Deprecated. use TransactionalSeamPhaseListener". I took out the SeamExtendedManagedPersistencePhaseListener and replaced it with TransactionalSeamPhaseListener in my faces-config.xml, and I still get

          org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: foo.bar.ips, no session or session was closed
          


          Any ideas? Does Seam-managed persistence context work with Facelets? LIEs have been the bane of ORM since Hibernate; surely they are solvable by now and I'm just making some dumb mistake?


          • 2. Re: Getting LazyInitializationErrors with a SMPC

            Yes, it works. It's just a new name. Are you using a seam managed persistence context?

            • 3. Re: Getting LazyInitializationErrors with a SMPC
              smokingapipe

               

              "norman.richards@jboss.com" wrote:
              Yes, it works. It's just a new name. Are you using a seam managed persistence context?


              Yes, definitely. Here's the scenario, which is just like basically every other web application on the planet: A user goes to a login-page, logs in, the "User" object is stored in the session, and then the user can access a bunch of protected pages that display and manipulate the "user" object. Just what every web app anywhere does.

              Here's what I've got:

              <!DOCTYPE faces-config PUBLIC
               "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
               "http://java.sun.com/dtd/web-facesconfig_1_1.dtd">
              
              <faces-config>
              
               <application>
               <view-handler>org.jboss.seam.ui.facelet.SeamFaceletViewHandler</view-handler>
               </application>
              
               <!-- We use the Seam Managed Persistence Context (SMPC) to avoid -->
               <!-- the dreaded LazyInitializationExceptions -->
               <lifecycle>
               <phase-listener>org.jboss.seam.jsf.TransactionalSeamPhaseListener</phase-listener>
               </lifecycle>
              
              <!-- a couple of converters also, removed for brevity -->
              
              </faces-config>


              and the LoginAction bean, with some boilerplate deleted:

              @Stateful
              @Name("login")
              public class LoginAction implements Login {
              
               @In(required=false) @Out(required=false)
               private Customer customer;
              
               private String name = null;
              
               private String password = null;
              
               @In(create=true)
               private transient FacesMessages facesMessages;
              
               /** Injected not created */
               @In(create=true) EntityManager smpc;
              
               public String gatewayLogin() {
               if(smpc == null) {
               logger.severe("No entity manager was found, so this will not work.");
               return "/internal-error.jsp";
               }
              
               if(password == null) return "internal-login";
              
               if(name == null) return "internal-login";
              
               password = password.trim().toLowerCase();
              
               final List<Customer> results = smpc.createQuery("from Customer where " +
               "name = :name and password = :password")
               .setParameter("name", name)
               .setParameter("password", getPassword())
               .getResultList();
               if (results.isEmpty()) {
               logger.info("no such customer was found");
               facesMessages.add("invalidLogin",
               new FacesMessage("The username or password was invalid"));
               customer = null;
               } else {
               customer = results.get(0);
               logger.info("About to log in this customer: " + customer);
               return "/customer/index";
               }
               }
              


              And persistence.xml:
              <persistence>
               <persistence-unit name="foo">
               <jta-data-source>java:/FooDS</jta-data-source>
              
               <properties>
               <property name="hibernate.hbm2ddl.auto"
               value="update"/>
              
               <property name="jboss.entity.manager.factory.jndi.name"
               value="java:/EntityManagerFactories/smpcData"/>
              
               </properties>
               </persistence-unit>
              </persistence>
              


              And components.xml:

              <components>
              
               <component name="org.jboss.seam.core.init">
               <property name="debug">true</property>
               <property name="jndiPattern">Foo/#{ejbName}/local</property>
               <property name="myFacesLifecycleBug">false</property>
               </component>
              
               <!-- 120 second conversation timeout -->
               <component name="org.jboss.seam.core.manager">
               <property name="conversationTimeout">120000</property>
               </component>
              
               <!-- We can use Seam-managed persistence context to avoid -->
               <!-- dreaded LazyInitializationExceptions -->
               <component name="smpc"
               class="org.jboss.seam.core.ManagedPersistenceContext">
               <property name="persistenceUnitJndiName">java:/EntityManagerFactories/smpcData</property>
              
               </component>
              
              </components>
              


              And finally, the Customer class itself, trimmed:

              @Entity
              @Name("customer")
              @Scope(ScopeType.SESSION)
              @Roles({@Role(name="createCustomer",scope=ScopeType.CONVERSATION)})
              public class Customer extends Person implements Serializable {
              



              Anyway, after I do the login, the "customer" is properly set in the HTTP session, as you would expect, but when I go to access some collection from the customer object, I get a LIE. I'm totally baffled by this, because I thought that SMPC was supposed to nearly eliminate LIEs.

              Thanks for any help.


              • 4. Re: Getting LazyInitializationErrors with a SMPC

                You need an extended conversation context.

                Use @Begin in your source or <s:link propagation="begin"> to begin a conversation. An SMPC will be setup in the conversation context where you would be able to avoid LIE, for that particular conversation.

                Seam's debug page (i.e. debug.seam) is helpful.

                • 5. Re: Getting LazyInitializationErrors with a SMPC
                  smokingapipe

                  Ah, that is indeed the problem. SMPC is CONVERSATION scoped, not SESSION scoped. I need to get that "customer" object into a conversation, which I will probably do with a @Begin method on the link. Easy to do.

                  Wow that really tripped me up. Now I'm going to try that, but it seems obviously correct.

                  • 6. Re: Getting LazyInitializationErrors with a SMPC

                    After re-reading your post, I may think you might not require an extended conversation context if it's a simple action.

                    What happens if you try to access "createCustomer" object instead of "customer" object during the render phase (i.e. facelets .xhtml) ?

                    As well you said that the customer object is setup fine in HttpSession, but can you verify that it is also in the conversation context?

                    You need to verify that the temporary conversation context created by Seam is not destroyed just before you access the customer object(which usually happens at the end of the render phase). Enable seam debugging: org.jboss.seam=debug and report what you find.

                    • 7. Re: Getting LazyInitializationErrors with a SMPC

                      Glad that it work out well for you.

                      • 8. Re: Getting LazyInitializationErrors with a SMPC

                        Just to lend some flavor to this, the key points are that to avoid a LIE, an entity has to be attached to an active persistence context and there has to be a be an active transaction.

                        The seam managed persistence context is one that has it's lifecycle associated with a conversation. As long as the conversation remains active, the persistence context is active and you will never get a LIE. (well, assuming there is a tx, which is what the transactional phase listener provides)

                        I assume the reason you were failing is that your PC was not in long-running conversation. As soon as your request ended, your short-lived PC died and your entity became detached and you could no longer access unloaded relations. Note that it doesn't strictly matter whether the entity is stored in the session or in the conversation - it matters that the PC for the original conversation is still active. It's obviously not a bright idea to store a conversation-scoped entity in the session, but it would work.


                        (I hope that didn't confuse the issue any more)


                        • 9. Re: Getting LazyInitializationErrors with a SMPC
                          smokingapipe

                           

                          "norman.richards@jboss.com" wrote:
                          I assume the reason you were failing is that your PC was not in long-running conversation.


                          Right, that is the problem. I load the customer into the session-scope, and that works. Now I want to display some collections that are referenced by the customer object on some other pages. That won't work because there's no conversation and thus no conversation-scoped SMPC.

                          It's obviously not a bright idea to store a conversation-scoped entity in the session, but it would work.


                          Ok, then how do I handle this? I've got a Customer in the session, and now I need to start a conversation with the same customer entity in it.

                          I could have a conversation-scoped bean with some other field, like conversationCustomer. I could then do:

                          conversationCustomer = entityManager.merge(customer);


                          but that seems wrong because I'm not really doing a merge.

                          What do you think of this? Surely this is a fairly common situation, where there is a user object that's in a session, and then we need to have that user attached to a PC so we can display collections from that user? There must be some standard approach to this?


                          • 10. Re: Getting LazyInitializationErrors with a SMPC
                            smokingapipe

                            Oh, another thing I noticed, according to: http://docs.jboss.com/seam/1.1GA/reference/en/html/xml.html, I can create a session-scoped SMPC, but it says: "This example creates a session-scoped Seam-managed persistence context (this is not recommended in practice):". It seems like the session-scope is the natural scope of "user" type objects, so shouldn't it be natural to have a session-scoped SMPC for those objects to be attached to?

                            I'm just trying to figure out what's the standard / best way to do these things.

                            • 11. Re: Getting LazyInitializationErrors with a SMPC
                              smokingapipe

                              Ok, what I am doing is I am using a SFSB (temp. conversation scope) to act as an intermediary when needed, and it's working fine (of course) and it's not cumbersome or unnatural. I'll use a real conversation bean (with Begin and End) when I come to some part like building up a complex report or query, where the user might want to try it, refine it, and repeat, and might want to have different queries being developed in different windows or tabs.

                              And I'm now starting to delve into Facelets. One cool thing I have noticed about Facelets is that I now have Seam debugging available. Now I need to figure out how to use it for templating or whatever else it's for.

                              One question about Facelets compared to JSP: How does the performance compare? I know JSP gets turned into a Servlet class, which gets compiled and classloaded, so it runs as fast as any compiled Java code. How about Facelets? Do they get XML-parsed every time they display, or are they cached in some compiled state?

                              • 12. Re: Getting LazyInitializationErrors with a SMPC

                                I'm not a persistence expert - so you'll probably want to ask Gavin when he gets back. In general I try to be careful about session-level data. Sessions generally live a long time, and session data can easily go stale. (a search result, or something) You'll often find that the best strategy is to reload the data from the database on each request to make sure you have uptodate objects. You take the object into a conversation for the purpose of some sort of multi-screen editing transaction.

                                So, what you might consider doing for the customer is to store the id in the session and use a seam wrapper object to retrieve actual customer as needed. You won't have to worry about a LIE because your object won't detach from the PC until the end of the request. And, you'll always have the most current data. If you are worried about efficiency, you can configure your second level cache to deal with reads.

                                But, as I said, I'm no persistence guru.

                                • 13. Re: Getting LazyInitializationErrors with a SMPC

                                  On the facelets question, remember that the ultimate goal of any view technology in JSF is to produce a UI model. In the JSP case, you have a JSP which generates a .java, compiles to a .class. The servlet is then loaded into to memory. When a request comes in, the servlet excutes and produces a UI model for JSF to use. With facelets, the .xhtml file is compile to a UI model in memory for JSF.

                                  Which do you think is more efficient?

                                  • 14. Re: Getting LazyInitializationErrors with a SMPC
                                    smokingapipe

                                    I now have Facelets working and am moving pages over from JSP-style <% include(... to using proper Facelet Templates. I have look at various template systems (both Java and PHP) and this one is the best. It produces well-formed output every time and lets me easily define a real page template that's totally separate from the functional parts of the pages. This is pretty cool. Now I'm understanding why people here are so hot on Facelets. It's far beyond JSP.

                                    I wish I had started building my app with seam-gen or something like that because the config hell has been the nightmare of this project. Getting past that, I'm seeing some amazing capabilities here.

                                    1 2 Previous Next