-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 3 posts ] 
Author Message
 Post subject: asymmetry between saving and deleting detached objects
PostPosted: Thu Aug 04, 2005 8:02 am 
Newbie

Joined: Wed Aug 03, 2005 9:10 am
Posts: 3
Hibernate version: 3.0.5

I am interested (out of curiosity) in your opinions about an asymmetry between saving and deleting detached objects. Concretely, in the context of Container Managed Transactions in J2EE Session Beans, there is a difference between session.merge() and session.delete() in the way a StaleObjectStateException is propagated. When deleting an object that has been deleted by another transaction, the StaleObjectStateException is thrown when the TM calls beforeCompletion() on Hibernate's FlushingEventListener.

Conversely, when calling merge() with a stale object, the merge() method detects the staleness of the object and throws a StaleObjectStateException immedediately, long before the TM comes into play.

This is logical because merge() but not delete() is explicitly defined as carrying the state of a detached object to a persistent object. But it has a somewhat unfortunate side-effect. In our J2EE projects, we want to hide Hibernate behind session facades. Web clients only see interfaces of persistent (but detached) objects, and talk to a remote facade for managing their life cycle. By our standards, web applications must handle concurrent update situations. To that end we define a checked exception, ConcurrentUpdateException. Now we could implement a session facade method for saving User objects as follows:

Code:
public User saveUser(User user) throws ConcurrentUpdateException {
       
        if (user == null) {
            throw new IllegalArgumentException("No user specified");
        }

        Session s = HibernateUtil.getFactory().getCurrentSession();

        try {
            return (User) s.merge((UserImpl)user);

        } catch(StaleObjectStateException e) {
            this.sessionContext.setRollbackOnly();
            throw new ConcurrentUpdateException(e);
        }
    }
When the supplied object is stale, this method sets the transaction to rollback and informs the client of the problem. A similar approach with deleteUser() does not work:

Code:
    public void deleteUser(User user) throws ConcurrentUpdateException {
       
        if (user == null) {
            throw new IllegalArgumentException("No user specified");
        }

        Session s = HibernateUtil.getFactory().getCurrentSession();

        try {
            s.delete(user);
           
        } catch(StaleStateException e) {
            //Never reached!
        }
    }


The exception is never catched, because it is thrown from inside beforeCompletion() which is called by the TM after the above method returns. The TM propagates the StaleStateException to the client, wrapped up in a RemoteException.

Of course this is not insuperable: we simply check manually that the object still exists by substituting the following for the try/catch block in the previous:

Code:
            UserImpl u2 = (UserImpl)s.get(UserImpl.class, user.getId());
            if (u2 != null) {
                s.delete(u2);

            } else {
                this.sessionContext.setRollbackOnly();
                throw new ConcurrentUpdateException("User object has been deleted by someone else");
            }


It's just not pretty (symmetrical). So my question is: why isn't there a special delete method for detached objects alongside delete(). Or better still: would be possible and desirable to change the semantics of delete() so that the difference becomes transparent? I would be interested in any thoughts you may want to share. Thanks,

Jan Voskuil


Top
 Profile  
 
 Post subject:
PostPosted: Thu Aug 04, 2005 9:17 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
Quote:
When deleting an object that has been deleted by another transaction, the StaleObjectStateException is thrown when the TM calls beforeCompletion() on Hibernate's FlushingEventListener

Only half right. The exception occurs during flush processing, which outside of explicit flush calls will occur during beforeCompletion().

So just use an explicit flush.

Code:
public void deleteUser(User user) throws ConcurrentUpdateException {
       
        if (user == null) {
            throw new IllegalArgumentException("No user specified");
        }

        Session s = HibernateUtil.getFactory().getCurrentSession();

        try {
            s.delete(user);
            s.flush();  // <- NOTICE THIS!!!!!!
        }
        catch(StaleStateException e) {
            //Never reached!   <- NO LONGER TRUE BECAUSE OF ABOVE!!!!!
        }
    }


Top
 Profile  
 
 Post subject:
PostPosted: Thu Aug 04, 2005 9:27 am 
Newbie

Joined: Wed Aug 03, 2005 9:10 am
Posts: 3
Thanks, you're right...

_________________
Jan Voskuil


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 3 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.