views:

265

answers:

1

Hi, I have a web-application which uses hibernate and for some reason every thread (httprequest or other threads related to queueing) uses a different session. I've implemented a HibernateSessionFactory class which looks like this:

public class HibernateSessionFactory {
private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();
private  static Configuration configuration = new AnnotationConfiguration();    
private static org.hibernate.SessionFactory sessionFactory;
static {
    try {
        configuration.configure(configFile);
        sessionFactory = configuration.buildSessionFactory();
    } catch (Exception e) {}
}
private HibernateSessionFactory() {}
public static Session getSession() throws HibernateException {
    Session session = (Session) threadLocal.get();
    if (session == null || !session.isOpen()) {
if (sessionFactory == null) {
    rebuildSessionFactory();//This method basically does what the static init block does
}
session = (sessionFactory != null) ? sessionFactory.openSession(): null;
threadLocal.set(session);
}
   return session;
}
//More non relevant methods here.

Now from my testing it seems that the threadLocal member is indeed initialized only once when the class is first loaded by the JVM but for some reason when different threads access the getSession() method they use different sessions. When a thread first accesses this class (Session) threadLocal.get(); will return null but as expected all other access requests will yeild the same session. I'm not sure how this can be happening as the threadLocal variable is final and the method threadLocal.set(session) is only used in the above context (which I'm 99.9% sure has to yeild a non null session as I would have encountered a NullPointerException at a different part of my app).

I'm not sure this is relevant but these are the main parts of my hibernate.cfg.xml file:

<hibernate-configuration>
<session-factory>
<property name="connection.url">someURL</property>
<property name="connection.driver_class"> com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
<property name="dialect">org.hibernate.dialect.SQLServerDialect</property>

<property name="hibernate.connection.isolation">1</property> 

<property name="hibernate.connection.username">User</property>
<property name="hibernate.connection.password">Password</property>
<property name="hibernate.connection.pool_size">10</property>

<property name="show_sql">false</property>
<property name="current_session_context_class">thread</property>
<property name="hibernate.hbm2ddl.auto">update</property>

<property name="hibernate.cache.use_second_level_cache">false</property>
<property name="hibernate.cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<!-- Mapping files -->

I'd appreciate any help granted and of course if anyone has any questions I'd be happy to clarify. Ittai

+5  A: 

Are you aware of the purpose of ThreadLocal?

From the docs:

This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).

You're getting a different Hibernate session for each thread precisely because that's what your code says to do.

Now, we can't really comment on whether this a good thing or not - although in many cases it's reasonably sensible. After all, you wouldn't want two threads to share the same session and interact with each other's transactions, would you?

Jon Skeet
first of all thank you. Secondly sadly enough I was not aware of the purpose of `ThreadLocal` and thank you. I see your point regarding the transactions and threading but the problem I'm currently facing is that one thread retrieves an entity and updates it. And another different thread is looking at this entity and does not see the change. Appreciate any thoughts about this.
Ittai
Well, then you need to either lock access to the shared entity, and/or send a notification to other viewers when the shared entity changes.
Matt
@Matt assuming I have many entities with the mentioned scenario is there a more structured procedure/design pattern/library which can help me do this? Also what do you mean by send a notification to other viewers? evict? (i though this is a session specific method)
Ittai
I am not familiar with Hibernate, specifically, but if you want other viewers to see changes to this shared data, you will need to fire an event when this data is updated. Viewers can listen to this event so that they will know that the data has changed and can act accordingly (reload the data, reconcile changes, etc). I guess that's just the observer pattern.
Matt
Are you working with long lived sessions? Hibernate sessions "should" be limited to an http request. Also, hibernate sessions are not thread safe, neither are the objects loaded from that session. Don't try to share them accross several threads.
Guillaume
@Guillaume Yes I am. I think I need to reinvestigate my approach to the whole combination of multithreading-http requests-JMS. thanks
Ittai