Announcement Announcement Module
Collapse
No announcement yet.
A way to handle Hibernate's LazyLoading with remoting Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • A way to handle Hibernate's LazyLoading with remoting

    Hello,
    I’m not sure if what I’m going to ask belongs here, if it doesn’t please let me know where should I put it.
    I’m looking for comments/suggestions on a problem I had and the solution I implemented.

    The application I work on has a 3 tier architecture:

    Eclipse RCP Client -- RMI --> Service Layer --> Dao Layer --> Hibernate --> DB

    What was really annoying me was the “depth” level of the object graph I was fetching from the DB. For example, in some scenario I needed a customer with all its relations and in other scenario I just needed its name and address. Being a RMI “jump” away hibernate’s lazy loading didn’t work, and I didn’t want to provide lots of method in the service interface to control this parameter, but neither did I want to send unnecessary big objects through the network.
    So what I did is to pass an Initializer from the client to the service layer to initialize the needed relations after the objects were retrieved from the DB, but before the transaction was commited. This extra parameter is passed as a Thread Local(client) - RemoteInvocation - Thread Local(server) attribute to avoid polluting the business interface.

    I have left some details out to keep this post readable, so please ask if there’s anything missing.

    Any comments will be greatly appreciated.
    Regards,
    Federico.

  • #2
    Hi Frederico.

    That sounds like a very interesting solution. It seems transparent which is a great benefit. How do you handle the initializers? Do you have some way to set it up per method on the client side?

    Jess

    Comment


    • #3
      Hello Jess, thanks for your answer.
      I set up the initializers per method call as a thread local variable just before making the invocation. Then a special RemoteInvocationFactory is responsible for putting the initializer as an extra attribute in the remoteInvocation and cleaning the thread local variable.
      Do you want me to post some code?

      Regards,
      Federico.

      Comment


      • #4
        Actually it would be nice to look at the code. But I am wondering more about the initializers. How do you do the code for them? Do you do an anonymous implementation (like done with ActionListener)? Or do you have a set of these that have names, eg. 'CustomerCollectionInitializer', 'CustomerNoCollectionInitializer'? Hope this makes sense....

        Jess

        Comment


        • #5
          Hi,

          I've been doing something similar for a while now and have found it works well. I haven't done anything like the threadlocal approach you talk about. I tend to consider the graph hydration level as part of the service contract so have no problem in including it in the interface.

          Maybe to address the other questions. I have a concept of a HydrationSpecification that contains two arrays, one of prefetch levels the other of post fetch levels. Each class in my domain model exposes a number of allowed levels. These level classes use OGNL to specify the actual properties which allows for quite complex loading situations (eg nested collections) to be handled quite well. My dao's then use the hydration specification by i) using the prefetch to add fetchMode's to the constructed criteria and ii) by using the post fetch to call initialize.

          Jonny

          Comment


          • #6
            Here’s one of them:

            public class PersonRoleInitPlan implements InitializationPlan<AbstractPerson> {
            public void init(AbstractPerson o) {
            Hibernate.initialize(o.getRoles());
            }
            }

            I could have initiliazed the roles collection by “iterating” it (to avoid coupling the domain with hibernate), for example:

            public void init(AbstractPerson o) {
            o.getRoles().size();
            }


            The anonymous approach seemed nice to me, but I’m using RMI, and using anonymous classes means it will also send the top level class through the network, something I’d like to avoid, so I ended with named initializers.

            Comment


            • #7
              Originally posted by jwray
              Hi,

              Maybe to address the other questions. I have a concept of a HydrationSpecification that contains two arrays, one of prefetch levels the other of post fetch levels. Each class in my domain model exposes a number of allowed levels. These level classes use OGNL to specify the actual properties which allows for quite complex loading situations (eg nested collections) to be handled quite well.

              Jonny
              Hello Johny,
              That´s interesting because it adds another level of customization to the dao. How do your classes expose the allowed levels?

              Federico.

              Comment


              • #8
                Just via public static final class variables. For example,
                Code:
                public static final ObjectGraphHydrationSpecification TIME_READS_WELL = 
                    new AssayPlateHydrationSpecification("htpPlateReads.{htpTimeReads.{well}}");
                The string passed to the constructor is an OGNL construct. This one means for all members of the collection htpPlateReads access the collection htpTimeReads and for all members of that collection get the well.

                Typically, each domain model class will have multiple allowed hydration levels. Some are valid both pre and post fetch, some only post fetch. The client of the services then passes the hydration levels it needs to the service. In this way the division of responsibilty is clear. Domain model classes are responsible for defining allowed levels. Service clients are responsible for specifying which levels they require. DAO's are responsible for interpreting the levels and doing whatever is needed to get the data.

                Originally posted by fschroder
                Hello Johny,
                That´s interesting because it adds another level of customization to the dao. How do your classes expose the allowed levels?

                Federico.

                Comment


                • #9
                  Interesting topic. I am working on a project that will need to support both Web and rich clients. One approach I've been pondering about lately is to advise the domain model classes deployed to the client side so that methods like getRoles() would lazily and transparently make a RMI call to get the role objects - similar to how Hibernate implements the lazy-loading on the server side. I haven't got around to actually prototype it yet, so I guess the performance implication is still hard to say, which of course would be the biggest concern.

                  Comment


                  • #10
                    I could be wrong but this almost sounds like it might lead to one of the initial problems that occured with people using entity beans in ejb. Too many remote calls for data access leading to awful performance, which in turn lead to the DTO pattern that basically advises locally populating an object with data before shipping across a wire.

                    Jonny

                    Originally posted by manifoldronin
                    Interesting topic. I am working on a project that will need to support both Web and rich clients. One approach I've been pondering about lately is to advise the domain model classes deployed to the client side so that methods like getRoles() would lazily and transparently make a RMI call to get the role objects - similar to how Hibernate implements the lazy-loading on the server side. I haven't got around to actually prototype it yet, so I guess the performance implication is still hard to say, which of course would be the biggest concern.

                    Comment


                    • #11
                      Originally posted by jwray
                      I could be wrong but this almost sounds like it might lead to one of the initial problems that occured with people using entity beans in ejb. Too many remote calls for data access leading to awful performance, which in turn lead to the DTO pattern that basically advises locally populating an object with data before shipping across a wire.
                      Definitely true. That's why I said the performance would remain the biggest concern. Although I don't expect it to be as bad as entity beans. There are some techniques to relieve the pain - those references most frequently referenced can be 'touched' (thus pre-fetched) on the server side, the client can leverage its own cache, etc. I'm even thinking about using hibernate on the client side as well, backed by an in-memory hsqldb as the cache. This way perhaps even the DAO's can also be reused on both sides.

                      Comment


                      • #12
                        Yep, I think a large part of the problems with enitity beans came from using them while ignoring the fact that data fetching was happening remotely. You're obviously not doing that here though.

                        I'm using the Hibernate second level cache on the server side and I prepopulate it with certain objects at application startup. For example, object collections that end up populating a list in a dialog, and I keep meaning to expand this approach, it works well. I like your idea of a client side cache, I can see a couple of places it might well work in my application, I should look into it in more detail.

                        On a more general note, I've found remote client tuning ends up being a number of trade offs between memory, speed etc. On the other hand, in most places in my application I've found users are happy with a little delay as long as they are told what's going on via a progress indicator and something as simple as a Fetching ... message. So, a lot of the time I don't invest that much energy in finding the optimal trade off result, unless there's a glaring problem.

                        Originally posted by manifoldronin
                        Definitely true. That's why I said the performance would remain the biggest concern. Although I don't expect it to be as bad as entity beans. There are some techniques to relieve the pain - those references most frequently referenced can be 'touched' (thus pre-fetched) on the server side, the client can leverage its own cache, etc. I'm even thinking about using hibernate on the client side as well, backed by an in-memory hsqldb as the cache. This way perhaps even the DAO's can also be reused on both sides.

                        Comment


                        • #13
                          Originally posted by manifoldronin
                          Interesting topic. I am working on a project that will need to support both Web and rich clients.
                          The project I work on also has a web client. The good thing about the proposed solution is that it works in the context of a remote invocation and doesn’t affect local calls, so it will be friendly with patterns like “open session in view”.

                          Originally posted by manifoldronin
                          I'm even thinking about using hibernate on the client side as well, backed by an in-memory hsqldb as the cache.
                          Using hibernate in the client this way sounds new to me, wouldn’t you need a replicated clustered cache to do that? Does hsqldb do that?

                          Comment


                          • #14
                            Originally posted by fschroder
                            Using hibernate in the client this way sounds new to me, wouldn’t you need a replicated clustered cache to do that? Does hsqldb do that?
                            oh, no, no, that's not what I meant. It doesn't make sense to cluster caches on server and clients. I just want to use a client-side cache to speed up frequent getter access. There are clearly a couple of assumptions:

                            1. all the write operations would still need to go stright through to some server-side service interface, which would also be the transaction boundary.

                            2. the nature of the access to the objects being cached can afford a short period of possible out-of-sync data, say, 3 minutes. From my experience, navigation on a particular object instance tends to follow a burst pattern, i.e., access to this instance and its related ones becomes intense during a short period time. E.g., when a button is clicked, or when a screen control is populated. During this short period, the volatility of the data is usually not a serious issue (remember we are still talking about read operations).

                            And it can be any cache implementation, not having to be hibernate+hsqldb. I was thinking of them just so that the DAO's themselves can then probably be reused on both sides.

                            Comment


                            • #15
                              Originally posted by fschroder
                              Here’s one of them:

                              public class PersonRoleInitPlan implements InitializationPlan<AbstractPerson> {
                              public void init(AbstractPerson o) {
                              Hibernate.initialize(o.getRoles());
                              }
                              }

                              I could have initiliazed the roles collection by “iterating” it (to avoid coupling the domain with hibernate), for example:

                              public void init(AbstractPerson o) {
                              o.getRoles().size();
                              }


                              The anonymous approach seemed nice to me, but I’m using RMI, and using anonymous classes means it will also send the top level class through the network, something I’d like to avoid, so I ended with named initializers.
                              Hi,

                              I work on a 3-tiered app with Java clients and a Spring/Hibernate server acessed over RMI, so your approach is of great interest to me. However, I can't figure it out with so few code. Would you mind giving us a more detailed exemple, with a sample domain object and a bit of client code?

                              I'd be grateful for that. Regards,
                              Baptiste

                              Comment

                              Working...
                              X