13 Replies Latest reply on Feb 4, 2008 2:19 PM by amitvk

    eager vs. lazy fetching

    eiben

      Hi,

      in my entities I defined my joined tables as being lazy; however, if I want to display a lot of detailed information it might be a good idea to retrieve the data using eager fetching. Is there any way to set the fething mode programmatically for certain methods?

      In this respect: I have a web-GUI, where the data is retrieved in a servlet and then the presentation is being rendered by a jsp-page using

      getServletContext().getRequestDispatcher(targetPage).forward(request, response);


      Problem: I want to iterate over a list of items and I want to display sub-items as well, unfortunatly those items are not yet fetched (lazy), and since I "redirected" the processing to the jsp-page the entity-manager is no longer available.

      Has anyone a solution for this problem?

        • 1. Re: eager vs. lazy fetching
          mazz

          You cannot programatically set the eager/lazy setting. You either set it to EAGER and have it always fetch, or you set it to lazy and for those things (like servlets) that need to force an eager fetch, they would need to call into some SLSB or some such thing that performs a FETCH JOIN query.

          • 2. Re: eager vs. lazy fetching
            eiben

             

            "mazz@jboss.com" wrote:
            You cannot programatically set the eager/lazy setting. You either set it to EAGER and have it always fetch, or you set it to lazy and for those things (like servlets) that need to force an eager fetch, they would need to call into some SLSB or some such thing that performs a FETCH JOIN query.


            OK, kinda feared something like that. How do I do FETCH JOIN query?

            • 3. Re: eager vs. lazy fetching
              mazz

              There's a lot of resources on the 'net that explain FETCH JOINs and its syntax.

              Bill Burke's EJB3 book also has a chapter on JPA Queries that is good.

              Google "FETCH JOIN". e.g. Here's a hibernate page that may be helpful.

              http://www.hibernate.org/hib_docs/reference/en/html/queryhql.html

              • 4. Re: eager vs. lazy fetching
                eiben

                OK, thanx. So I got this thingy working.

                But now I have something like this, which also gives me an error, that the items are not yet fetched ...

                Itemgroup myItemGroup = getItemDispatcher().findItemGroup(Integer.parseInt(request.getParameter("id")));
                
                Collection<Item> myig = myItemGroup.getItems();
                


                and the ItemDispatcher does something like this:
                @Stateless
                public class ItemDispatcherBean implements IItemDispatcher
                {
                 public Itemgroup findItemGroup(int id)
                 {
                 return em.find(Itemgroup.class, id);
                 }
                //...
                }
                


                I think that's a little bit odd ... I thought this could be fetched "on-demand" ...

                • 5. Re: eager vs. lazy fetching
                  mazz

                  So, Itemgroup has an items relationship that is lazy loaded. Your findItemGroup is only loading in the Itemgroup entity without doing any fetch joining or eagerly loading of that relationship, hence your error.

                  you will need to have another method, "findItemGroupWithItems" or something like that. In that method you execute a query that does a JOIN FETCH, rather than use find(). Or you can do a find and then (still in findItemGroupWithItems method) immediately force the loading to happen by calling itemgroup.getItems().size() (this causes a second round trip to the database - not as efficient but easy to code up).

                  • 6. Re: eager vs. lazy fetching
                    eiben

                     

                    "mazz@jboss.com" wrote:
                    So, Itemgroup has an items relationship that is lazy loaded. Your findItemGroup is only loading in the Itemgroup entity without doing any fetch joining or eagerly loading of that relationship, hence your error.


                    well, but I though when I access the items-collection the first time, the data get's loaded from the database.


                    you will need to have another method, "findItemGroupWithItems" or something like that. In that method you execute a query that does a JOIN FETCH, rather than use find(). Or you can do a find and then (still in findItemGroupWithItems method) immediately force the loading to happen by calling itemgroup.getItems().size() (this causes a second round trip to the database - not as efficient but easy to code up).


                    Do I have to make the call to the size attribute within my session-bean? It seems that it's not working as expected when I access that attribute from within my servlet.

                    I read in an book, that a workaround would be to use SFSB instead of SLSB, where the SFSB uses an extended persistent-context. This way I could make repeated requests to my entity as long as I don't remove the SFSB.

                    So I tryed something like this:

                    Itemgroup myItemGroup = getItemDispatcherExtended().findItemGroup(Integer.parseInt(request.getParameter("id")));
                    
                    Collection<Item> myig = myItemGroup.getItems();
                    getItemDispatcherExtended().finished();
                    


                    where the ItemDispatcherExtended is a SFSB

                    @Stateful
                    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
                    public class ItemDispatcherExtendedBean implements IItemDispatcherExtended
                    {
                     @PersistenceContext(type = PersistenceContextType.EXTENDED) //, unitName = "business")
                     private static EntityManager em;
                    //...
                    }
                    


                    • 7. Re: eager vs. lazy fetching
                      mazz

                      Every time you call "find" you are getting back a new attached instance initialized according to the lazy-loading properties of your entity, i.e. it will not have any relationship info that is set to LAZY. Even if you called that method from that servlet before. I believe the same behavior occurs even if you call find() again immediately after the first find() in your session bean! The semantics of the find() method seem very clear to me on this point. Every time you call it, you get a new instance initialized according to the lazy-loading properties.

                      Yes, obviously you need to call the size() method within your session bean because its only within there that you are still inside the scope of your entity manager and transaction. Once you return back out into your servlet layer, you are no longer within the scope of the entity manager and it cannot manage your relationships anymore (i.e. you are detached at this point). So, if you don't initialize your relationship collections prior to leaving your session bean, your servlet won't have it.

                      Side note: you might want to look into JBoss Seam - it provides the integration it sounds like you are looking for - that is, a seamless integration between the EJB/JPA layer and the servlet/JSF presentation layer.

                      • 8. Re: eager vs. lazy fetching
                        eiben

                        I see ... even though I'm not 100% satisfied with this answer :). As you figured, I wouold like to have a better integration into the presentation-layer.

                        Then again: I'm currently working on a research-project, where I want to compare two different web-application-platforms (java vs. php), so I'm writing a little app on both platforms. Using Seam would seam to me too much work ... since I'm quite new to java, and I can't spend all of my to coding java (even though, that's the most fun part to me!)

                        • 9. Re: eager vs. lazy fetching
                          eiben

                          but wait ... when I'm using SFSB and I make repeated calls, wouldn't that be like making those calls all from the same session bean? I mean ... I though using a SFSB in conjunction with an extended entity-manager should avoid that entities get detached.

                          • 10. Re: eager vs. lazy fetching
                            mazz

                            using extended persistence contexts can work also - which is how JBoss Seam does it, I believe.

                            • 11. Re: eager vs. lazy fetching
                              eiben

                              well ... seems not. Or I'm doing something wrong :(

                              That's what I read in some book, that using extened persistence and SFSB should overcome the problem of detaching entities.

                              hmm ...

                              • 12. Re: eager vs. lazy fetching
                                amitvk

                                Another way I found useful was using the OOP concepts to load the mappings or just the core table vlaues.

                                Have the main table mapped in the superclass and the mappings that you want to load sometimes in the subclass.

                                e.g. If the table name is Customer.

                                have 2 entities Customer, and CustomerWithMapping
                                CUstomerWithMapping extends Customer.

                                Define all the Customer column values in Customer base class.

                                Define all the lazy mappings in CustomerWithMapping class with EAGER fetch..

                                If you just want to get the core table data find on the Customer. If you want it with mappings call e.g. Select e from CustomerMapping where e.CustomerNumber =1.

                                I used the Customer class when I want to display list of customers and CustomerWithMapping when working on a specefic Customer.

                                Hope this helps.

                                P.S the only problem I found was that you cannot use em.find to get the CustomerWithMapping Object as it tries to load the superclass.
                                In that case use JPQL - Select e from CustomerMapping where e.CustomerNumber =1

                                • 13. Re: eager vs. lazy fetching
                                  amitvk

                                  Another way I found useful was using the OOP concepts to load the mappings or just the core table vlaues.

                                  Have the main table mapped in the superclass and the mappings that you want to load sometimes in the subclass.

                                  e.g. If the table name is Customer.

                                  have 2 entities Customer, and CustomerWithMapping
                                  CUstomerWithMapping extends Customer.

                                  Define all the Customer column values in Customer base class.

                                  Define all the lazy mappings in CustomerWithMapping class with EAGER fetch..

                                  If you just want to get the core table data find on the Customer. If you want it with mappings call e.g. Select e from CustomerMapping where e.CustomerNumber =1.

                                  I used the Customer class when I want to display list of customers and CustomerWithMapping when working on a specefic Customer.

                                  Hope this helps.

                                  P.S the only problem I found was that you cannot use em.find to get the CustomerWithMapping Object as it tries to load the superclass.
                                  In that case use JPQL - Select e from CustomerMapping where e.CustomerNumber =1