-->
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.  [ 12 posts ] 
Author Message
 Post subject: Session.refresh() inadvisable
PostPosted: Tue May 15, 2007 3:13 pm 
Beginner
Beginner

Joined: Mon Nov 20, 2006 2:46 pm
Posts: 32
Hello,

I have come across a situation in my application that appears to call for using Session.refresh(). However, I see that the javadoc for that method indicates it is "inadvisable" -- so I thought I would post here to understand what the potential problems with Session.refresh() are.

Let me describe my scenario. Essentially I have a work queue where I want to ensure that each work item is assigned only once. A user logs into the system, selects a work item and then claims it. This is all done via a web application.

When the user selects a work item, it is placed in the web session. Then the user is given the option of claiming the work item. Suppose user A and user B are both viewing work item 1. Now user A claims work item 1, changing it's status in the database. Then user B submits the page to claim work item 1 as well. The obvious solution (at least to me) is as follows:

Code:
// Lock the work item to protect against simultaneous access
session.lock(workItem, LockMode.UPGRADE);
// Now we need to check that workItem has not been claimed
// but the workItem object from the web session might be stale
session.refresh(workItem);
if (workItem.getStatus().equals(Status.UNASSIGNED))
{
    // the work item is available, proceed to claim it
}
else
{
    // the work item is not available, return an error to the user
}


I considered getting a new instance of the work item, however that means that the one in the web session is stale. Another approach would be to run a query to see if the work item is available, but that seems like unnecessary extra work.

Is there something wrong with the approach above? Is there a problem with using Session.refresh() in this manner?

Thanks,
Ray


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 15, 2007 11:04 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
The javadoc states that it's inadvisable to use refresh in a long-running session. You're writing a webapp. Presumably you're not using long sessions. Feel free to use refresh.

However, I think that your method is over the top. So long as you're using transactions, you shouldn't have to do any of what you describe. You could use select-before-update on the relevant class mapping, or preemtive locking/versioning, but short sessions and transactions should be enough for most cases.

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 16, 2007 1:27 pm 
Beginner
Beginner

Joined: Mon Nov 20, 2006 2:46 pm
Posts: 32
tenwit wrote:
The javadoc states that it's inadvisable to use refresh in a long-running session. You're writing a webapp. Presumably you're not using long sessions. Feel free to use refresh.

However, I think that your method is over the top. So long as you're using transactions, you shouldn't have to do any of what you describe. You could use select-before-update on the relevant class mapping, or preemtive locking/versioning, but short sessions and transactions should be enough for most cases.


tenwit, thanks for your reply. I'm not sure I've grokked everything about it however.

The javadoc I've read says it is "inadvisable to use this to implement long-running sessions" which I understood to mean don't use it as a work-around when long-running sessions are not available, e.g. in a web app.

If I understand select-before-update correctly, that means that Hibernate will determine if the object is dirty by doing a select before executing the update() when a detached object is re-attached via update(). This helps Hibernate determine if the object is different and if an UPDATE is really required. However, in the case I outlined above, my code needs to know the value of a particular attribute in the database and perform different business logic. So I do not see how that helps.

I'm afraid I do not understand what you mean by pre-emptive locking.

Versioning as I understand it looks promising. In the scenario I outlined with versioning enabled, the second update to the work item would be based on a stale instance and would be disallowed so Hibernate would throw an exception. That might be the way to go here.

Thanks again.


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 16, 2007 5:30 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
RayDeCampo wrote:
The javadoc I've read says it is "inadvisable to use this to implement long-running sessions" which I understood to mean don't use it as a work-around when long-running sessions are not available, e.g. in a web app.

I don't see the correlation between preferring to not use it in long sessions, and forbidding using it in short sessions. I'd go with the opposite understanding: the javadocs are commenting only on long sessions, you are free to make up your own mind about using refresh in short sessions.

The "normal" use of refresh(), if there is one, is to undo changes that you've made to a persistent in-memory object. This can easily happen in both long and short sessions, and it's ok to do that (e.g. you've copied values from the submitted form to the persistent object, validated it, failed validation, so you undo the changes. Acceptable). The use that the javadocs is suggesting is inappropriate is to load an object (say, a User object), keep it in memory permanently, then calling refresh() on it every time a user submits an action. It is more appropriate to discard the User object as each action finishes, and reload it from its ID at the start of the next action.

The select-before-update thing will work only if the relevant business business logic is sanity checked in your hibernate mapping or in triggers/check constraints in the database. So it is not suitable in most circumstances, but it is appropriate in some situations, which is why I mentioned it.

My bad about the use of "pre-emptive", I meant optimistic. Vrsioning is one kind, but there are other kinds too. Look through the refdocs for "optimistic locking", it's mentioned in several chapters.

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 16, 2007 7:06 pm 
Beginner
Beginner

Joined: Mon Nov 20, 2006 2:46 pm
Posts: 32
tenwit wrote:
RayDeCampo wrote:
The javadoc I've read says it is "inadvisable to use this to implement long-running sessions" which I understood to mean don't use it as a work-around when long-running sessions are not available, e.g. in a web app.

I don't see the correlation between preferring to not use it in long sessions, and forbidding using it in short sessions. I'd go with the opposite understanding: the javadocs are commenting only on long sessions, you are free to make up your own mind about using refresh in short sessions.

The "normal" use of refresh(), if there is one, is to undo changes that you've made to a persistent in-memory object. This can easily happen in both long and short sessions, and it's ok to do that (e.g. you've copied values from the submitted form to the persistent object, validated it, failed validation, so you undo the changes. Acceptable). The use that the javadocs is suggesting is inappropriate is to load an object (say, a User object), keep it in memory permanently, then calling refresh() on it every time a user submits an action. It is more appropriate to discard the User object as each action finishes, and reload it from its ID at the start of the next action.


This is essentially the use in the code I posted, except that it is not done on every user action, only a particular one and the object is kept in the web session, not permanently. I'd like to understand why that is considered a poor use of refresh if someone could enlighten me.

Quote:
The select-before-update thing will work only if the relevant business business logic is sanity checked in your hibernate mapping or in triggers/check constraints in the database. So it is not suitable in most circumstances, but it is appropriate in some situations, which is why I mentioned it.

My bad about the use of "pre-emptive", I meant optimistic. Vrsioning is one kind, but there are other kinds too. Look through the refdocs for "optimistic locking", it's mentioned in several chapters.


Thanks, I did read up on versioning and I will be pursuing that solution (at least for now)...


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 16, 2007 7:16 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
There's probably as many answers to why it's a bad idea as there are attempts to do it, but here's a few to start with:
  1. What happens if the object you're refreshing has been deleted on disc? You get a hibernate exception, that's what. Which means you have to discard your session and start a new one. Arg. Solution: look up the object each time. No hibernate exception thrown when you fail to find it, you can throw your own exception that doesn't mess with hibernate internals.
  2. What happens when some completely unrelated hibernate exception happens, and your session has been discarded, and you have a new one? Your refresh throws a new hibernate exception. See above for drawbacks.
  3. You're hanging onto more system resources than you strictly need. Not good practice.
  4. It's a global variable. Nuff said.

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject:
PostPosted: Fri May 18, 2007 11:27 am 
Beginner
Beginner

Joined: Mon Nov 20, 2006 2:46 pm
Posts: 32
See my comments below. In case it doesn't come through in my questions, I really want to understand this, please don't take it as an attack.

tenwit wrote:
There's probably as many answers to why it's a bad idea as there are attempts to do it, but here's a few to start with:
  1. What happens if the object you're refreshing has been deleted on disc? You get a hibernate exception, that's what. Which means you have to discard your session and start a new one. Arg. Solution: look up the object each time. No hibernate exception thrown when you fail to find it, you can throw your own exception that doesn't mess with hibernate internals.


In this case my business logic dictates that the record would not be deleted.

Quote:
  • What happens when some completely unrelated hibernate exception happens, and your session has been discarded, and you have a new one? Your refresh throws a new hibernate exception. See above for drawbacks.


  • I'm having trouble understanding this one. Why does the refresh() throw an exception? What is the difference if there has already been a cycle of exception-new session?

    Quote:
  • You're hanging onto more system resources than you strictly need. Not good practice.


  • How does calling refresh() "hang onto system resources"? Perhaps you mean by keeping the instance around; still it is just a POJO, it does not have any system resources. I am using the "open session in view" pattern so the instance will become detached at the end of the request in any case, no?

    Quote:
  • It's a global variable. Nuff said.


  • It's a web session "variable", not global. Are you saying never keep anything in the web session? Reload it from the database on every request? That seems wasteful.

    Thanks,
    Ray


    Top
     Profile  
     
     Post subject:
    PostPosted: Sun May 20, 2007 6:33 pm 
    Expert
    Expert

    Joined: Thu Dec 23, 2004 9:08 pm
    Posts: 2008
    RayDeCampo wrote:
    In this case my business logic dictates that the record would not be deleted.

    Ah, but the general advice was written as general advice, not as an instruction on how to implement your particular business case.

    RayDeCampo wrote:
    I'm having trouble understanding this one. Why does the refresh() throw an exception? What is the difference if there has already been a cycle of exception-new session?

    Because the object you're refreshing was loading in session A. A reference to this handle is recorded in the object. Since there was an exception in session A, and since you're following the hibernate rule that you must discard and recreate a session if an HibernateException is thrown, you are now in session B. sessionB.refresh(objectFromSessionA) will not work. In fact, it'll throw another HibernateException, causing you to throw away sessionB and create sessionC.

    RayDeCampo wrote:
    How does calling refresh() "hang onto system resources"?

    It doesn't. A long session does. It refers to a cache (or two), a JDBC connection, a SessionFactory, and so forth.

    Quote:
    It's a web session "variable", not global.

    These is a possibility of miscommunication here. Do you mean that it lasts the duration of a single web request? If so, that's a short session, not a long session. refresh() to your heart's content. If you mean that it lasts the duration of one user's log-in (potentially several hours or longer), then that's a long session, and so the session is a global variable. Global != infinite, it just means that any "normal" code can expect to have it available, it's not scope-limited to a particular class or method.

    Hope that this is making things clearer for you.

    _________________
    Code tags are your friend. Know them and use them.


    Top
     Profile  
     
     Post subject:
    PostPosted: Mon May 21, 2007 9:24 am 
    Beginner
    Beginner

    Joined: Mon Nov 20, 2006 2:46 pm
    Posts: 32
    tenwit wrote:
    RayDeCampo wrote:
    In this case my business logic dictates that the record would not be deleted.

    Ah, but the general advice was written as general advice, not as an instruction on how to implement your particular business case.


    Agreed.

    tenwit wrote:
    RayDeCampo wrote:
    I'm having trouble understanding this one. Why does the refresh() throw an exception? What is the difference if there has already been a cycle of exception-new session?

    Because the object you're refreshing was loading in session A. A reference to this handle is recorded in the object. Since there was an exception in session A, and since you're following the hibernate rule that you must discard and recreate a session if an HibernateException is thrown, you are now in session B. sessionB.refresh(objectFromSessionA) will not work. In fact, it'll throw another HibernateException, causing you to throw away sessionB and create sessionC.


    OK, I think I see what you are saying here. I assume the same does not apply if no exception was thrown in session A.

    tenwit wrote:
    RayDeCampo wrote:
    How does calling refresh() "hang onto system resources"?

    It doesn't. A long session does. It refers to a cache (or two), a JDBC connection, a SessionFactory, and so forth.

    tenwit wrote:
    RayDeCampo wrote:
    It's a web session "variable", not global.

    These is a possibility of miscommunication here. Do you mean that it lasts the duration of a single web request? If so, that's a short session, not a long session. refresh() to your heart's content. If you mean that it lasts the duration of one user's log-in (potentially several hours or longer), then that's a long session, and so the session is a global variable. Global != infinite, it just means that any "normal" code can expect to have it available, it's not scope-limited to a particular class or method.


    First, I disagree that a reference in a web session variable is a global variable. This is just terminology but I think it is important. There are many differences between a reference in a web session and a global variable, but I digress.

    Second, I think you have misunderstood what is in the web session. I have not placed a Hibernate session in the web session. I am using the "open session in view" pattern. I have placed a bean whose persistence is managed by Hibernate in the web session. So the bean lasts through many requests, but each request has its own session.

    Finally, let me try to re-frame the discussion a little. Consider the following situation. You have data in a bean in the web session. You are using the "open session in view" pattern. The bean was originally loaded by Hibernate. For some requests, it is important that we have the most up-to-date data from the database for this bean. What is the best practice here?


    Top
     Profile  
     
     Post subject:
    PostPosted: Mon May 21, 2007 5:30 pm 
    Expert
    Expert

    Joined: Thu Dec 23, 2004 9:08 pm
    Posts: 2008
    RayDeCampo wrote:
    Second, I think you have misunderstood what is in the web session. I have not placed a Hibernate session in the web session. I am using the "open session in view" pattern. I have placed a bean whose persistence is managed by Hibernate in the web session. So the bean lasts through many requests, but each request has its own session.

    Aha, I have spotted the problem. It is confusion over the word "session". A long session, at least on the hibernate forums, means a long hibernate session. OpenSessionInView means a short session: it is created when the user clicks a button, and it disappears after the web page is redrawn. I guess that when you are talking about a long session, you mean a long session of the user being at the keyboard, or somesuch. When you put a persistent entity in the web session, it remains persistent until the hibernate session is closed as you leave the OpenSessionInView filter. I don't know if you your web session lasts longer than that, but if it does, the hibernate recommendation about not using refresh() to implement long (hibernate) sessions does not apply, becausae you are no longer in the same session. You now have a new problem, that of reattaching your detached entity when the next hibernate session is created,

    Oh and about the global variable thing: yes it is just terminology. No, it's not important, not even to this discussion.

    _________________
    Code tags are your friend. Know them and use them.


    Top
     Profile  
     
     Post subject:
    PostPosted: Tue May 22, 2007 8:55 am 
    Beginner
    Beginner

    Joined: Mon Nov 20, 2006 2:46 pm
    Posts: 32
    tenwit wrote:
    RayDeCampo wrote:
    Second, I think you have misunderstood what is in the web session. I have not placed a Hibernate session in the web session. I am using the "open session in view" pattern. I have placed a bean whose persistence is managed by Hibernate in the web session. So the bean lasts through many requests, but each request has its own session.

    Aha, I have spotted the problem. It is confusion over the word "session". A long session, at least on the hibernate forums, means a long hibernate session. OpenSessionInView means a short session: it is created when the user clicks a button, and it disappears after the web page is redrawn. I guess that when you are talking about a long session, you mean a long session of the user being at the keyboard, or somesuch.


    No, I was careful to modify the word "session" with web when I was referring to the web session and not the Hibernate session, mindful of the forum in which I was posting. I think the bigger source of confusion are the words "implement a long running session" within the Hibernate javadocs; you have interpreted them to mean within a long running session, whereas I have interpreted them to mean recreating the functionality of a long session without using them.

    tenwit wrote:
    When you put a persistent entity in the web session, it remains persistent until the hibernate session is closed as you leave the OpenSessionInView filter. I don't know if you your web session lasts longer than that, but if it does, the hibernate recommendation about not using refresh() to implement long (hibernate) sessions does not apply, becausae you are no longer in the same session.


    Of course the web session lasts longer than one request, that is the whole idea behind it.

    tenwit wrote:
    You now have a new problem, that of reattaching your detached entity when the next hibernate session is created,


    Yes, that is how I started down this path.


    Top
     Profile  
     
     Post subject:
    PostPosted: Tue May 22, 2007 6:04 pm 
    Expert
    Expert

    Joined: Thu Dec 23, 2004 9:08 pm
    Posts: 2008
    Not being a web developer, I don't know much about web sessions. For all I knew, that's the term used to describe a single web request+response. I think I'm better informed now though, so thanks for that.

    Anyway, the answer to your original question is: you don't have a long (hibernate) session, so you can safely ignore the suggestion to not use refresh() to implement long (hibernate) sessions.

    Perhaps a new thread about how to use load(entity.getId()), merge(entity), replicate(entity, ReplicationMode.OVERWRITE) and so forth would be more helpful.

    _________________
    Code tags are your friend. Know them and use them.


    Top
     Profile  
     
    Display posts from previous:  Sort by  
    Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 12 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.