Announcement Announcement Module
Collapse
No announcement yet.
SpringSessionContext and long sessions? (concurrency issue?) Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • SpringSessionContext and long sessions? (concurrency issue?)

    Hi all,

    I have a JSF web application utilizing Hibernate. It runs with the OpenSessionInViewFilter, which works perfectly fine for my needs.

    In my applicationContext.xml, I have a plain DataSource, a SessionFactory and some bean definitions. Nothing special about that (really!).

    Code:
        <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
            <property name="jndiName" value="java:comp/env/jdbc/foo"/>
        </bean>
    
        <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
            <property name="dataSource" ref="dataSource"/>
            <property name="packagesToScan">
                <list>
                    <value>foo.entities</value>
                </list>
            </property>
            <property name="hibernateProperties">
                <props>
                    <prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
                </props>
            </property>
        </bean>
    
        <tx:advice id="myAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="find*" read-only="true"/>
                <tx:method name="create" propagation="REQUIRED" rollback-for="Exception"/>
                <tx:method name="delete" propagation="REQUIRED" rollback-for="Exception"/>
                <tx:method name="save" propagation="REQUIRED" rollback-for="Exception"/>
                <tx:method name="*" rollback-for="Exception"/>
            </tx:attributes>
        </tx:advice>
    
        <aop:config>
            <aop:pointcut id="myPointcut"
                          expression="execution(* foo.dao.*.*(..))"/>
            <aop:advisor advice-ref="myAdvice" pointcut-ref="myPointcut"/>
        </aop:config>
    
        <bean id="myTransactionInterceptor" class="org.springframework.orm.hibernate3.HibernateInterceptor">
            <property name="sessionFactory" ref="sessionFactory"/>
        </bean>
       
        <bean id="myDao" class="foo.MyDao">
            <property name="sessionFactory" ref="sessionFactory"/>
        </bean>
    MyDao.java looks something like this:

    Code:
    public class MyDao {
    
        private SessionFactory sessionFactory;
    
        public MyEntity findById(Long id) {
            return (MyEntity) sessionFactory.getCurrentSession().get(MyEntity.class, id);
        }
    
        public List<MyEntity> getAllMyEntities {
            Query q = sessionFactory.getCurrentSession().getNamedQuery("MyEntity.getAllMyEntities");
            return q.list();
        }
    
        public void setSessionFactory(SessionFactory sessionFactory) {
            this.sessionFactory = sessionFactory;
        }
    }
    This works fine and Spring gives me Hibernate access through the same Hibernate session in each HTTP request. (Nice!)

    ----

    What makes things funny is this:

    In the same web application, I have created a context listener, which is executed by the container (Tomcat 6) on application startup. This context listener looks into Spring and creates a java.util.Timer object for each entity. I.e. something like this:

    Code:
            MyDao myDao = (MyDao) WebApplicationContextUtils.getWebApplicationContext(context).getBean("myDao");
            
        	List<Timer> serverTimers = new ArrayList<Timer>();
        	List<MyDao> myEntities = myDao().getAllMyEntities();
        	for (MyEntity myEntity : myEntities) {
                Timer t = new Timer("Timer " + myEntity.getId());
                ServerTimerTask serverTimerTask = new ServerTimerTask(myEntity.getId());
                t.schedule(serverTimerTask, 5000, 20000);
                serverTimers.add(t);
        	}
    This creates a number of threads, which runs inside the application. What makes things go bad is that I cannot seem to have each thread bind its own Session to Hibernate.

    I have tried this inside each thread:
    Code:
    TransactionSynchronizationManager.initSynchronization();
    Session session = SessionFactoryUtils.getSession(sessionFactory, true);
    TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
    
    // Do lots and lots of Hibernate/Dao stuff....
    
    TransactionSynchronizationManager.unbindResource(session.getSessionFactory());
    SessionFactoryUtils.closeSession(session);
    TransactionSynchronizationManager.clearSynchronization();
    However this doesn't seem to work, and the threads closes sessions belonging to other threads. (Seems like a concurrency issue!)

    Does anyone have a clue on how to register long Hibernate sessions in SpringSessionContext so the above code will just run inside one single Hibernate session?

    Best regards,
    Morten
    Last edited by silverjam; Jul 15th, 2009, 08:22 AM.

  • #2
    It was indeed a concurrency issue.

    I missed a ThreadLocal variable to store the session in.

    This is my helper class that does the job in case other people need to do the same:

    Code:
    package com.mycompany.lib.util.spring;
    
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.orm.hibernate3.SessionFactoryUtils;
    import org.springframework.transaction.support.TransactionSynchronizationManager;
    
    public class HibernateSessionUtils {
    
        private static final Logger logger = LoggerFactory.getLogger(HibernateSessionUtils.class);
    
        private static ThreadLocal<Session> longSession = new ThreadLocal<Session>();
    
        public static Session openLongSession(SessionFactory sessionFactory) throws Exception {
            try {
                logger.trace("Opening long Hibernate session...");
                TransactionSynchronizationManager.initSynchronization();
                Session session = SessionFactoryUtils.getSession(sessionFactory, true);
                longSession.set(session);
                logger.trace("Opened long Hibernate session " + session.hashCode() + ".");
                return session;
            }
            catch (Exception e) {
                throw new Exception("Could not open long Hibernate session.", e);
            }
        }
    
        public static void closeLongSession() {
            try {
                Session session = longSession.get();
                logger.trace("Closing long Hibernate session " + session.hashCode() + "...");
                SessionFactoryUtils.closeSession(session);
                TransactionSynchronizationManager.clearSynchronization();
                longSession.remove();
            }
            catch (Exception e) {
                logger.warn("An error occurred while closing long Hibernate session!", e);
            }
        }
    
    }
    The above code works with Hibernate 2.5.6.

    Best regards,
    ~Morten

    Comment

    Working...
    X