1 2 Previous Next 15 Replies Latest reply on Dec 9, 2009 12:09 PM by Jun Li

    Clone hibernate object within conversation still get LazyInitException

    Jun Li Newbie

      OK. What I want to do is to implement 'cancel' function.


      As entities are used in the view directly, therefore whatever changes made from web pages are reflected in entities, it seems to me that the only way to cancel the change is to reload the entity from database.


      But for some use cases, we can't simply do a reload, as the business operation consists of multiple steps something like a wizard. I thought in those cases, in order to achieve cancel, I have to save the status of the entity to somewhere.


      So, what I did was to serialize/clone the object graph . I knew that with this approach that those lazy initilised properties needs to be taken care of by customer converter/mapper etc without seam.


      My question was, with seam conversation scope, there will always be a session available, why do I still get LazyInitExecption when I try to persis the deserialized object?


      Do I still need to use customer converters?


      Thx.

        • 1. Re: Clone hibernate object within conversation still get LazyInitException
          Nikolay Elenkov Master

          Cloning/deserialization create a new instance, effectively detaching the object from the Session/Persistence context. That is why you are getting LIEs. Converters (generally) have nothing to do with this.


          I don't know how your model is organized, but generally you should be able to implement undo by reloading the root object from the DB. That way all intermediate state(adding/removing from collections, changing properties, etc.) will be lost, and you can start over. If you are saving intermediate results in your wizard, this gets tricky though: you have to trace your steps to implement undo, which is not always easy. You'd better use manual flush and only flush at the end of the wizard. Then you can implement undo using Session.evict/clear() + reload from DB.


          HTH

          • 2. Re: Clone hibernate object within conversation still get LazyInitException
            Jun Li Newbie

            Thanks for the reply Nikolay.


            Simple reload can't solve my problem, as I have to save intermediate results.


            I understand that the object is detached from session, but why can't it be re-attached back after deserialized if there is session available, do I have to call session.replicate?


            I'm using manual flush, but the cancel function I need to implement is not cancelling the whole process but intermediate status. At the moment, I'm tracing all the possible user operations which is such a pain to write. Just thought if I could save(clone/serialize) the object and then re-attach it back to session, it would be a much cleaner approach.

            • 3. Re: Clone hibernate object within conversation still get LazyInitException
              Tim Evers Master

              I don't know the complexity or the details of your needs but sometimes a different approach makes your situation a bit easier.


              Don't bind your components directly to an attached entity. Use a DTO for each section of the wizard so that all a cancel needs to do is rebuild the DTO for that part of the wizard.


              Partial rollbacks are never much fun and I would be trying to avoid them if I could!



              I tried to write something to get around your problem, but everytime I started thinking about it I realised that there's really no nice way of doing this.

              • 4. Re: Clone hibernate object within conversation still get LazyInitException
                Nikolay Elenkov Master

                Allan Li wrote on Dec 07, 2009 23:26:


                I understand that the object is detached from session, but why can't it be re-attached back after deserialized if there is session available, do I have to call session.replicate?



                Well, I'm not 100% sure about this, but technically you should be able to reattach. The problem is that you have two instances of the same entity (same PK): on in the session, and the one you cloned. Not sure how Hibernate behaves in this case if you try to re-attach, you might have to evict first and re-attach. In any case, there is no easy way to do this, AFAIK. If you object graph is really that complicated, DTO's might be indeed an easier solution.


                • 5. Re: Clone hibernate object within conversation still get LazyInitException
                  Jun Li Newbie

                  Thanks for the input guys.


                  DTO is something I try to avoid. To me, manage DTOs takes probably the same amount effort as cloning/copy entities.


                  I'll find some time to try evit and re-attach to see how hibernate behaves.

                  • 6. Re: Clone hibernate object within conversation still get LazyInitException
                    Tim Evers Master

                    Maybe I'm missing something here but, how is evicting and then reataching going to help you.


                    Let's assume you have 3 parts to your wizard. So you enter page 1. Evict the entity. Make your changes and go to part 2. Now what? Do you copy/clone your entity from page 1 and make updates to the copy? And so then you copy/clone the entity from page 2 to use in page 3. Is this what you are talking about?


                    Because if you use the same evicted object in all pages of your wizard then you are not doing partial rollbacks and you might as well just re read from the database every time.



                    What's your psuedo code for how you plan to handle your wizard?

                    • 7. Re: Clone hibernate object within conversation still get LazyInitException
                      Jun Li Newbie

                      Yes, I was going to clone the object for each step and pass it on.


                      Anyway, I tried em.merge hoping that the cloned/deserialized object can be re-attached to the session and won't throw LIE, but it didn't work.

                      • 8. Re: Clone hibernate object within conversation still get LazyInitException
                        Tim Evers Master

                        You did evict before cloning right? Otherwise you are serializing your attached state and thus LIE's will happen.

                        • 9. Re: Clone hibernate object within conversation still get LazyInitException
                          Jun Li Newbie

                          OK, I got it work!


                          What I found was if I only use part of the cloned object graph it seems to be fine.


                          Here is the code,




                          public class Client implements Serializable {
                          
                              @OneToMany
                              private List<Name> names;
                          
                              @OneToMany
                              private List<Address> addresses;
                          
                              // setters and getters
                          }
                          
                          @Scope(Conversation)
                          public class ClientNameAction {
                          
                            private Client clonedClient;
                          
                            @In @Out
                            private Client client;
                          
                            // Called before name is managed
                            public void init() {
                               clonedClient = (Client)SerializationHelper.clone(client)
                            }
                          
                            /* other methods add/modify/delete names on client.names  */
                          
                            // called when user wants to cancel the changes
                            public void cancel() {
                               // replace messed up names with saved/cloned names
                               client.setNames(clonedClient.getNames());
                               // client = clonedClient (this won't work)
                            }
                          }
                          
                          @Scope(Conversation)
                          public classs ClientAddressAction {
                             // some methods to manage addresses
                          
                          }
                          
                          



                          ClientNameAction and ClientAddressAction are steps of the manage client process which is saved to db as one tx.

                          • 10. Re: Clone hibernate object within conversation still get LazyInitException
                            Nikolay Elenkov Master

                            Allan Li wrote on Dec 08, 2009 12:14:


                            OK, I got it work!

                            What I found was if I only use part of the cloned object graph it seems to be fine.



                            Why doesn't simply calling em.refresh(client) work? That should reload both client and names/addresses from the DB.

                            • 11. Re: Clone hibernate object within conversation still get LazyInitException
                              Jun Li Newbie

                              Why doesn't simply calling em.refresh(client) work? That should reload both client and names/addresses from the DB.

                              When user cancel address changes, we still need to keep the changes user made for names, they are different steps of one process.

                              • 12. Re: Clone hibernate object within conversation still get LazyInitException
                                Tim Evers Master

                                I don't want to discourage you in your work, but honestly I really think you are setting yourself up for some tough times. When your domain objects change and become more complex this whole clone/evict/partial rollback thing is going to come back and haunt you (or the guy that comes in after you).


                                Although I disagree with your approach, I wouldn't mind seeing the code out of professional curiosity :P


                                I still don't understand how the cloning/evict approach ends up being less work then a straight DTO for this wizard so maybe seeing the code will help.


                                Cheers,
                                Tim

                                • 13. Re: Clone hibernate object within conversation still get LazyInitException
                                  Nikolay Elenkov Master

                                  Allan Li wrote on Dec 09, 2009 06:56:


                                  When user cancel address changes, we still need to keep the changes user made for names, they are different steps of one process.


                                  I see. But then it's not really a wizard. You either finish the wizard and commit or you cancel everything. I guess you could simply flush after the name changes (1st step), go to address change (step 2) and if the user cancels, just reload from DB. That keeps the name changes and discards the address changes. Your use case might be more complicated than that though...


                                  • 14. Re: Clone hibernate object within conversation still get LazyInitException
                                    Jun Li Newbie

                                    I guess you could simply flush after the name changes (1st step), go to address change (step 2) and if the user cancels, just reload from DB.

                                    Did I say they are two steps of one process? If I had name changes flushed to DB, there is no way to cancel the changes if the whole process is cancelled, I can't keep the tx open for that long time. All changes are flushed within one tx when user click the final save button, there a few other steps.

                                    1 2 Previous Next