Announcement Announcement Module
Collapse
No announcement yet.
Hibernate and lazy load Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Hibernate and lazy load

    Hello,

    Here is my situation. I have three classes A, B, and C. Class A has a collection of B and B has a collection of C. I'm using Hibernate for persistence. I have mapped the collections as lazy - since most of them I don't need to retrieve the entire graph. I have DAO classes for each A, B, and C. Each DAO classes extends HibernateDaoSupport.

    My problem is, for one case I need to retrieve the entire graph. After rereading the the docs and the forums, I realize that HibernateDaoSupport opens and closes the session for me and to avoid a LazyInitializationException I need to reattach the object to a session. Fair enough.

    My question, is would it be a good idea to create an Intercepter on A.getB() and B.getC() that would make a call to Hibernate.initialize() ?

    If not, or if there is a better idea, could someone direct me to sample code? I've looked at the Spring samples but there does not appear to be an example of using Hibernate with lazy collections.

    Thanks.

  • #2
    You could explicitly touch the relationship methods within your DAO or the enclosing transaction from service layer. Or you could use a fetch join. I don't much like the interceptor approach: seems a bit too much magic vs these simpler techniques.

    Comment


    • #3
      I have read about two possible solutions to this problem:

      1) Look at the OpenSessionInView pattern that is on the hibernate website (you can also try searching this forum for the same pattern). Basically, this pattern will do essentially what you want so that your view can be totally naive as to the lazy loading of data. The drawback to this approach is that because the DAO/persistance will be accessed outside of the service/business tier you won't have transaction coverage over the access.

      2) The book "Pro Spring" describes a little bit about a lazy loading strategy but it involves having your presentation tier tell the business tier whether it needs the "whole" object loaded or not. Thus, in your example you would probably have a method in your business tier that would be like "getB( Integer bId, boolean loadAll )". As previously mentioned the drawback is that your presentation tier must have some knowledge as to whether it needs everything or not and you are essentially doing some amount of performance coding in the presentation tier AND your presentation tier needs to be smart enough to only access those lazy fields if you have done a loadAll. The pro of this solution is that it is still demarcated by the transactions in the business tier.

      Any thoughts/other solutions?

      Comment


      • #4
        Sweet... Rod Johnson... love your work

        Comment


        • #5
          Thanks for the quick replies

          Comment


          • #6
            Or you could use a fetch join
            Actually I tried that and it did not seem to work. I will go back and investigate that route. BTW, I'm fairly new to Hibernate.

            Thanks again.

            Comment


            • #7
              ok, I've added a method to my dao to retrieve with a fetch join

              Code:
              public List getAll(){
                 return getHibernateTemplate().find("select name from A " +
                              "join fetch B " +
                              "join fetch C ");
              This produces LazyInitializationException when I attempt to call A.getB()

              Looking into Hibernate in Action I read "Hibernate currently limits you to fetching just one collection eagerly". I am using Hibernate 2.1.8 not 3.

              If I change my getAll() to

              Code:
              public List getAll(){
                 return getHibernateTemplate().find("select name from A " +
                              "join fetch B " +
                              "join C ");
              then as expected I get LazyInitializationException when I call B.getC().

              So then is it fair to say that because of a limitation of Hibernate I need to find an alternative to the much simplier solution of using fetch join?

              Thanks.

              Comment


              • #8
                My problem is, for one case I need to retrieve the entire graph.
                What is that case for? Where does the case occurs and who is responsible for this case in first place (object wise)? Normally you use the same session as long as you have to read datas from the database. Mostly you go for the following rule: One request, one thread, one session. Also all related reading should be preferable done inside a single (possibly read only) transaction. Otherwise you might sitting in hells kitchen when you face unpredictable and unmanageable data inconsistency.


                My question, is would it be a good idea to create an Intercepter on A.getB() and B.getC() that would make a call to Hibernate.initialize() ?
                Sounds not that good to me. But I never used such code anyways, so I am not the right person to make a judgment. ;-)


                If not, or if there is a better idea, could someone direct me to sample code? I've looked at the Spring samples but there does not appear to be an example of using Hibernate with lazy collections.
                Can you tell us a bit more about the use-case and the related context, the need for such retrieval of the entire graph exist? Would help... . Also I would like to know how your transactions are managed. Do you use any caching strategy?


                Cheers,

                Martin (Kersten)

                Comment


                • #9
                  This thread might be of interest to you:
                  http://www.newsarch.com/archive/mail.../msg03823.html

                  Comment


                  • #10
                    If you knew there existed at least one B for each A then you could retrieve the entire graph with one query.

                    Code:
                    from B b join fetch b.a join fetch b.c
                    You are only allowed to eagerly fetch one collection in a query, but are able to "fetch as many one-to-one or many-to-one associations as you like".

                    Then you'll need to make the results distinct, then process the collection to get all the A's.

                    This might not be appropriate for every situation as I don't know how efficient all the collection processing will be for large result sets.

                    Comment


                    • #11
                      Martin,

                      Our domain and dao classes are being used by two applications one is a web app that uses our default mapping with lazy load. What I am working on is a command line app that needs to process every object of class A in the database (along with each associated collection).

                      maxjar,

                      Thanks for the link, I'll take a look.

                      I think I need to do some more reading on Hibernate before I waste anymore of anyone's time.

                      Thanks to everyone for the comments.

                      Comment


                      • #12
                        Would a HibernateInterceptor be useful in this situation?

                        Comment


                        • #13
                          Our domain and dao classes are being used by two applications one is a web app that uses our default mapping with lazy load. What I am working on is a command line app that needs to process every object of class A in the database (along with each associated collection).
                          The reason I asked, is just that I wonder myself if it is not possible to drive the use-case 'going through the graph and analyse stuff' even further towards the domain layer? I mean you seam to do work related to the domain model. Also you are likely to have this all to appear as a business transaction (special need to control transactions). This means that it is actually appearing to be a unit of work. And transactional stuff is normally an issue for the domain layer (one layer below the UI layer).


                          Cheers,

                          Martin (Kersten)

                          Comment


                          • #14
                            Ok I seem to be making some progress. Sometimes you try to make things more difficult than they need to be.

                            Within a method of my DAO

                            Code:
                            Session session = getHibernateTemplate().getSessionFactory().openSession();
                            Transaction tran = session.beginTransaction();
                            
                            List sites = session.find("from SiteConfig siteConfig " +
                                 "left join fetch siteConfig.routers router " +
                                 "left join fetch router.interfaces interfaces " +
                                 "where interfaces.monitored = ?", Boolean.TRUE, Hibernate.BOOLEAN);
                            
                            for &#40;int i = 0; i < sites.size&#40;&#41;; i++&#41; &#123;
                                 ISiteConfig siteConfig = &#40;ISiteConfig&#41; sites.get&#40;i&#41;;
                                 List routers = siteConfig.getRouters&#40;&#41;;
                                 for &#40;int j = 0; j < routers.size&#40;&#41;; j++&#41; &#123;
                                      IRouterConfig routerConfig =  &#40;IRouterConfig&#41; routers.get&#40;j&#41;;
                                      routerConfig.getInterfaces&#40;&#41;.size&#40;&#41;;
                                 &#125;
                            &#125;
                            
                            tran.commit&#40;&#41;;
                            session.close&#40;&#41;;
                            return sites;
                            I am no longer getting the LazyInitializationException. Now I need to figure out why in my test I'm getting three interfaces (two of which are null) for a given router when there is only one associated interface in the database. I guess I need to dig into Hibernate some more.

                            Comment


                            • #15
                              In case anyone else has a similiar problem.

                              I had router-interfaces mapped as a bi-directional list.

                              "Please note that Hibernate does not support bidirectional one-to-many associations with an indexed collection (list, map or array) as the "many" end, you have to use a set or bag mapping."

                              I changed the mapping to a bidirectional set and it works.

                              Comment

                              Working...
                              X