Announcement Announcement Module
No announcement yet.
States of Hibernate objects passed to a business method Page Title Module
Move Remove Collapse
Conversation Detail Module
  • Filter
  • Time
  • Show
Clear All
new posts

  • States of Hibernate objects passed to a business method

    I have a bunch of business objects that are wrapped by transaction proxies using TransactionProxyFactoryBean with a HibernateTransactionManager.

    If an object is passed to a business method, I don't know whether or not it is attached to a Hibernate session (and, thus, whether or not I can rely on lazy initialization), since I don't know how the caller got their hands on it.

    Is there a way I can find out of the object is attached to any open session? I know how see if it's part of the current method's session/transaction, but if it was initialized in a transaction further down in the call stack, how can I know?


  • #2
    Hibernate does not share an object between sessions so I can't see how your object could be attached to any session down the callstack. It either belongs to a session or it does not. Since the session is bound to the thread you could check if it belongs to an existing session.


    • #3
      Consider an example with two methods in different objects.

      methodA() is declared with PROPAGATION_REQUIRED.

      methodB() is declared with PROPAGATION_REQUIRES_NEW.

      Suppose methodA() loads a persistent object using Hibernate and passes it to methodB(). Now, methodB() doesn't know how the caller got the object, so it could be in a transient, detached, or persistent state. But methodB() requires that any properties it tries to access will be available (via lazy-loading or already in memory), so I'm trying to develop a DAO method that methodB() can use, which will call Session#lock in Hibernate if and only if the the object needs it.

      I can figure out if the object is transient by checking the ID for null, but I don't know how to check to see if it's detached or not. This may be more of a Hibernate question, but I'm posting it here because the whole transaction proxy thing adds another layer of complexity. There are multiple sessions involved (one per transaction), right?

      If there's a best practice to handle situations like this, I'm all ears.


      • #4
        You can't have the same object belong to two different sessions at the same time, so you can't pass in an object that belongs to an existing session and saveOrUpdate it in a new session. You would have to detach the object (using session.evict) before you pass it to methodB. Then you can attach the object to the new session, update it and then save it back. Upon return, methodA could reattach the object if needed and continue from there.

        As far as I know Hibernate has no call where you could find out whether an object belongs to an existing session.


        • #5
          Hmm, yeah, I guess that's one approach. In general, then, you'd need to evict any object before passing it to any other proxied business method and attach it afterwards. Kind of a pain.

          On the other hand, you'd really only need to do this when calling a method that starts a new transaction. Not exactly transparent.

          I'm relatively new to Spring. Is this the strategy most people use when passing persistent objects around between proxied Busienss Service Objects or am I doing something uncommon?


          • #6
            It's not really a Spring issue - it's all Hibernate related. I would say though that in general the objects should be saved to the database from the same transaction/session where they were retrieved. One exception here is when you detach an object and pass it to a separate layer of your application. When the object is returned, you can safely re-attach it to a new transaction since you know it is detached.

            Passing an object between one transaction into a second one persisting it in this new transaction is not a good practice. You can possibly change its values, but let the calling method save it using the original transaction.

            I don't know your use case here, but I would try to avoid this multi transaction approach if at all possible. The second transaction should be autonomous and not affect any objects that are not retrieved within the transaction.


            • #7
              Actually, I didn't say anything about changing or saving the object that gets passed into methodB(). I'm only trying to read from it. Nevertheless, I get an exception if I blindly try to attach it to the second transaction's session, since it's already attached to one.

              I do create and save different objects in methodB(), which is one reason for desiring a new transaction. But that's outside the scope of my problem.

              I guess I can see the need for having to evict an object before passing it to a method that starts a new transaction. I just wish it could happen behind the scenes so I don't have to think about it. It would be even cooler if the object was then automatically attached to the new session. Oh well.

              Thanks for the info.


              • #8
                Forgot that the original issue was the lazy loading I would say the same rules still applies. The caller is responsible for the objects it loads. This includes supplying data either as an object with lazy load available via an active session, or as an object that has the relevant collections pre-populated.

                Alternatively, you could retrieve a new (read-only) copy of the object within your new session and get the data from there. Detach and reattach seems a bit messy in this context.


                • #9
                  Originally posted by trisberg
                  Alternatively, you could retrieve a new (read-only) copy of the object within your new session and get the data from there. Detach and reattach seems a bit messy in this context.
                  I considered that, too. Only problem is that the object state might have changed when I reload it in the second transaction and I'll never know. (Risk is small, but still...)

                  I guess there's no getting around making the caller aware of the transaction semantics of the methods it's calling. The rule of thumb I'll use is that if the callee is going to suspend the current transaction, I'd better evict any object I pass into it and then attach it in the callee. (I'd prefer that to forcing the caller to initialize the entire object graph that the callee might use.)

                  I don't know much about AOP or the interceptors being leveraged by the TransactionProxyFactoryBean, but I wonder if it's possible to develop an interceptor that implements this logic so my code doesn't have to...?


                  • #10
                    I considered that, too. Only problem is that the object state might have changed when I reload it in the second transaction and I'll never know. (Risk is small, but still...)
                    The same would be true if you reattach the object, at least for anything lazyloaded.


                    • #11
                      Good point. I also don't want to have to reload objects that the caller has already loaded, though.


                      • #12
                        I finally got time to try this out.

                        Unfortunately, evicting the object doesn't work. Neither does calling clear() on the session. I still get "LazyInitializationException: Illegally attempted to associate a proxy with two open Sessions". (In my test case, it looks like it's complaining about an uninitialized collection in the object I'm passing, for what that's worth.)

                        So it seems like there is no safe way to pass a persistent object across transaction boundaries and still take advantage of lazy-loading in the new transaction. That's kind of a big limitation. I'm surprised I haven't seen this mentioned anywhere else.