-->
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.  [ 18 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: effective use of ThreadLocal and other questions
PostPosted: Sun Oct 05, 2003 11:24 pm 
Newbie

Joined: Sun Oct 05, 2003 4:29 pm
Posts: 14
Location: Toronto, Canada
Hey there,

I'm trying to understand the proper use of the ThreadLocal pattern in regards to Hibernate.

In an effort to learn Hibernate, i'm taking an web application I originally wrote in PHP/MySQL a few years ago, and turning it into a Java web app.

After looking through the documentation, forums, etc, I decided that I did not want to use the Open in View pattern, as in my opinion it seems more like an anti-pattern, and as I am a big fan of test driven design, it would complicate my testing options.

So my idea is to use a DAO type aproach where in an Hibernate aware class, I implement CRUD methods, etc. My question is, what is the proper approach to take in regards to Session management and usage of ThreadLocal? To be more specific,

Say I have a method in my Hibernate data access class that loads an item
Code:
public User loadUser(Long id) {}

Should I be obtaining my ThreadLocal object inside this method like so:
Code:
public User loadUser(Long id) {
Session session = ThreadLocalSession.currentSession();
Transaction tx = session.beginTransaction();
.... (etc)
ThreadLocalSession.closeSession();
}


Or, should I be passing in an instantiated Session into the method signature from another class which is responsible for manipulating each of the CRUD methods like so:
Code:
public User loadUser(Session session, Long id) {
   ....
  return (User)session.load(User.class, id);
}


Personaly I think the first way is the way to do it, since each method represents a unit of work, but i'm just not sure. I'm aware that I will need to prepare all of the data that I might need on the upmost layers (the web app) by initializing collections, etc, down at this level, which makes things like lazy loading almost worthless. Or does it?

I'm also guessing here that using JCL could problably help aleviate the pre loading problem, and i'm sure there are some clever tricks that can be used to help some of pre loading problems. Has anyone come up with a way to apply tricks like this (Hibernate.initialize(object), object.callMethod(), etc) in a way that 1) doesn't confuse the intent of the code, and 2) doesn't propagate the use of hibernate into other layers?

Thanks for reading. Any help/insight is appriciated.

John


Top
 Profile  
 
 Post subject:
PostPosted: Mon Oct 06, 2003 12:02 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
Quote:
since each method represents a unit of work

If you are absolutely certain that this will be the case, then why even use a ThreadLocal? Why not just open a session in each method? The ThreadLocal approach is only really useful in cases where multiple DAO methods must be accessed to perform a use case.

Quote:
which makes things like lazy loading almost worthless. Or does it?

depends on how complex/deep your domain model is. In general, it is still nice to map most things as lazy/proxied. It gives you very fine-grained control over exactly what gets loaded. You mention your User class. Say User is mapped to associate a Set of Group obejcts; and Group is mapped to associate a Map of privileges... If all this is set up as lazy, then when you load User for a detail screen, you can choose what needs to get initialized based on the requirements for the detail screen. If the detail screen needs to list the groups to which the user belongs, then go ahead and initialize it; if thats not needed for the detail screen, then don't.


Top
 Profile  
 
 Post subject: effective use of ThreadLocal and other questions
PostPosted: Mon Oct 06, 2003 7:42 am 
Newbie

Joined: Sun Oct 05, 2003 4:29 pm
Posts: 14
Location: Toronto, Canada
Steve,

Thanks for taking the time to reply.

I think I understand the semantics of using a ThreadLocal now. In my particular case my domain model is pretty simple, and each method will most likelly represent one unit of work. So opening/closing a session inside each one will do. If I end up needing to share a session between different methods, then it should be fairly simple to refactor.

As for the lazy loading, what you stated is also what I've come to the conclusion of. Having control over just what is returned/populated, etc, is what appeals to me. Again in my case I problably can get away with not using lazy loading, but some of these News objects can have 1000s of Comment objects, so it would be nice to control this. I just need to figure out how to load subsets of data into my collections.

For example, my User class has a collection of News. How would I initialize just the first 10 for example?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Oct 06, 2003 9:30 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
Have a look at the session.filter() method


Top
 Profile  
 
 Post subject:
PostPosted: Mon Oct 06, 2003 1:07 pm 
Newbie

Joined: Sun Oct 05, 2003 4:29 pm
Posts: 14
Location: Toronto, Canada
Steve,

Do you mean something like this:

For example (of the top of my head):
Code:
public User findUser(Long id) {
Session session = factory.getSession();
List result = session.find("from User as user where user.id = :userid", id, Hibernate.LONG);
User found = (User)result.get(0);

List = filteredCommentsList = session.filter (found.getComments(),"do something to find the 10 records I want");
found.setComments(filteredCommentsList)
return found;

}


Is that the way to do it? or can session.filter() be used in place of session.find() ?

Thanks,
John


Top
 Profile  
 
 Post subject:
PostPosted: Mon Oct 06, 2003 1:08 pm 
Newbie

Joined: Sun Oct 05, 2003 4:29 pm
Posts: 14
Location: Toronto, Canada
Further thought. Would the code idiom I just posted cause a second hit to the db?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Oct 06, 2003 3:19 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
Don't do this: found.setComments(filteredCommentsList) ... You'll be in for a nasty surprise when the session flushes.

Yes, filter will hit the database to resolve the filter query string.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Oct 06, 2003 7:14 pm 
Newbie

Joined: Sun Oct 05, 2003 4:29 pm
Posts: 14
Location: Toronto, Canada
Good tip!

Of to learn about filter(). Thanks


Top
 Profile  
 
 Post subject:
PostPosted: Mon Oct 06, 2003 8:36 pm 
Newbie

Joined: Sun Oct 05, 2003 4:29 pm
Posts: 14
Location: Toronto, Canada
Ok I don't get it....

All of the examples I see in the documentation have the filter() returning a collection. I don't understand how I need to apply a filter in my situation.

Code:
public User findUser(Long id) {
List results = session.find(from User user where user.id = :userid", id, Hibernate.LONG);

....
User user = results.get(0);

user.getNews() // the collection I want to filter.

Query query = session.createFilter(user.getNews(), "what goes in here?");

query.list() // how do I set this list to the collection in my domain object?

return user;

}

I think I understand the purpose, but I don't understand how I can initialize to a set number of elements my user.getNews() Set. An example would be great....

Thanks,
John


Top
 Profile  
 
 Post subject:
PostPosted: Mon Oct 06, 2003 9:52 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
Quote:
but I don't understand how I can initialize to a set number of elements my user.getNews() Set


You cannot initialize a set number of elements onto the domain object. The domain object simply knows about its associations/relationships to other domain entities, components, etc.

If you want to restrict the number of total available collection set to only a subset you have two options that I know of:
1) Use the where clause attribute of the collection mapping (its usage is outlined in the documentation);
2) utilize a UserType

If, like I think you are asking, you are trying to say "pre-initialize this collection to say 10 elements; but if I want all of them go ahead and get the rest... Thats not doable. You cannot have your cake and eat it too, as the saying goes. You need to choose one approach or the other: either map the collection as the full collection, or map it as the subset of the full collection.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Oct 06, 2003 10:56 pm 
Newbie

Joined: Sun Oct 05, 2003 4:29 pm
Posts: 14
Location: Toronto, Canada
Steve,

I understand what you are saying. I too was under the impression when i started down this path, that I could not have my cake and eat it too, but all this talk about session.filter() confused me.

I am still confused as to what the purpose of a collection filter is. What is a valid use case for applying a filter to a collection if I can't apply it to a mapped collection element in my domain object?

Thanks,
John


Top
 Profile  
 
 Post subject:
PostPosted: Tue Oct 07, 2003 10:15 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
Quote:
What is a valid use case for applying a filter to a collection if I can't apply it to a mapped collection element in my domain object?


Really two scenarios come to my mind:
1) Web apps where the controllers are directly interacting with Hibernate;
2) Service layer mediating calls to Hibernate on behalf of front ends using a DTO pattern.

In the first, the controller could filter the news items (in your case) for say the last ten items and place those ten on the request for display.

In the second, population of the DTO's "recentNews" attribute could utilize the filter to obtain the last ten elements.

The reason to use filter in the first place is that it allows you to deal with only certain subsets of available data. Say one of your users could be associated with thousands of news items, and further that you want to do something like above. You'd pretty much have two options:
1) initialize the entire collection and then just pull the specific elements you are interested in. This will return the entire possible result set fo news items for that user and hydrate the News objects for each row in that association collection;
2) use a filter to filter the association collection to only the specific elements you are interested in. Then only those elements matching the filter are included in the jdbc result set and only element in that filtered result set are actually hydrated.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Oct 10, 2003 1:20 pm 
Newbie

Joined: Sun Oct 05, 2003 4:29 pm
Posts: 14
Location: Toronto, Canada
I get it now! ThreadLocal that is, i've given up on the filters for now :)

Anyway what I mean is that I now understand the usage of ThreadLocal.

I did as you sugested Steve, and opend a session in each of my CRUD type methods. At first it seemed just fine, but then I ran into use cases where I did have to have some objects loaded and saved in the scope of the same session/transaction. It seemed silly and wastefull to be opening a session to open or save a single object, which is fine if that is all you want to do, and in some cases that is all. However, once I started implementing my more complicated (if you can call them that) use cases, for example a user creating a new article, that I noticed that I need to create a user object, a category object, and finaly a news object.

Those 3 steps are a valid use case in my application, and it started to bother me that I was opening a session for each of those steps, when they really are part of the same "transaction".

So first I thought, ok i'll create a session using my ThreadLocalSession class, and pass the instance as a parameter into my CRUD methods. Well that looked pretty ugly. Then I finally figured it out (call me slow), I can just call ThreadLocalSession.currentSession() iINSIDE my CRUD method and it will use the same exact session that my "use case" method started. To explain it in code (totally made up and wont compile):
Code:
public  void buildNews(NewsCommand command) {
try {
Session session = ThreadLocalSession.currentSession(); //start here
Transaction tx = session.beginTransaction();
User user = (User)findEntity(userid); // crud method

... other crud methods ...

session.saveOrUpdate(news);
tx.commit();
} catch (HibernateException e) {
    tx.rollback();
} finally {
ThreadLocalSession.closeSession();
}
}

Inside findEntity():
Code:
public User findEntity(Long id) {
Session session = ThreadLocalSession.currentSession(); // same instance!
return session.find(....);
}


Doing things this way, allows me fine control over each of my "use case" methods, in that by having the session opend duing the execution of one of those methods, i can now control just what gets instantiated. Pretty neat.

Now my question... Where should transaction demarcation (beginTransaction(), commit(), rollback(), etc) occour? In my basic crud methods? Or in the more use case like methods that initiate the first opening of the session? I think the former is the correct anwser, but I would like to know what people are doing out there.

Also, am I correct to assume that by using the hibernate transaction API, I can later on plugin another transaction strategy without changing my code?

Hibernate rocks! I'm loving the fact how I can still have a properlly designed domain model, and now that I get it, have fine control over what I need to do. Pretty neat!

Thanks,
John


Top
 Profile  
 
 Post subject:
PostPosted: Fri Oct 10, 2003 5:15 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
Quote:
... Where should transaction demarcation

Depends on how your system is set up. Typically the layer directly above the DAO layer is the ideal candidate. Or take a look at the Spring framework for a way to introduce aspects or interceptors to your method calls to handle this.

Quote:
Also, am I correct to assume that by using the hibernate transaction API, I can later on plugin another transaction strategy without changing my code?

Thats true in every single respect excepting container managed transactions inside an EJB container. But aside from that scenario, you are correct.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Oct 10, 2003 5:39 pm 
Newbie

Joined: Sun Oct 05, 2003 4:29 pm
Posts: 14
Location: Toronto, Canada
Thanks again Steve!

On your last comment, if I at some point in time, want to have some of this code run inside an EJB container (say I move my DAOs into an EJB container as session beans), I would have to remove the transaction calls? Would they have to be replaced with session.flush() calls?

Just curious really. I dont intend in using an EJB Container anytime soon.

John


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 18 posts ]  Go to page 1, 2  Next

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.