Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate random "Session is closed error" with 2 databases

I have a requirement to use 2 different databases within single DAO class. One of the databases is read/write while the other is read only.

I have created 2 data sources, 2 session factories and 2 transaction managers (transaction manager for the read/write database is the platform transaction manager) for these databases. I am using @Transactional on the service method to configure Spring for transaction management.

We are getting random Session is closed! exceptions when we call sessionFactory.getCurrentSession() in the DAO class ( I can not always produce it, it sometimes works ok, sometimes gets error) :

org.hibernate.SessionException: Session is closed!
at org.hibernate.internal.AbstractSessionImpl.errorIfClosed(AbstractSessionImpl.java:133)
at org.hibernate.internal.SessionImpl.setFlushMode(SessionImpl.java:1435)
at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:99)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1014)

I don't have a requirement to use global transaction (XA), I just want to query 2 different databases.

I have read this thread, it suggests injecting two separate session factories in the DAO layer as we do now: Session factories to handle multiple DB connections

Also AbstractRoutingDataSource does not work for single Dao class as per this answer: https://stackoverflow.com/a/7379048/572380

Example code from my dao looks like this:

Criteria criteria = sessionFactory1.getCurrentSession().createCriteria(MyClass.class);
criteria.add(Restrictions.eq("id", id));
criteria.list();

criteria = sessionFactory2.getCurrentSession().createCriteria(MyClass2.class); // generates random "Session is closed!" error.
criteria.add(Restrictions.eq("id", id));
criteria.list();

I have also tried using "doInHibernate" method. But the session passed to it is also randomly throwing "Session is closed!" exceptions:

    @Autowired
    protected HibernateTemplate hibernateTemplate;

    @SuppressWarnings("unchecked")
    protected List<Map<String, Object>> executeStaticQuery(final String sql) {
        HibernateCallback<List<Map<String, Object>>> hibernateCallback = new HibernateCallback<List<Map<String, Object>>>() {
            @Override
            public List<Map<String, Object>> doInHibernate(Session session) throws HibernateException {
                SQLQuery query = session.createSQLQuery(sql);
                query.setResultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP);
                return query.list();
            }
        };
        return hibernateTemplate.execute(hibernateCallback);
    }
like image 225
nilgun Avatar asked Jan 23 '18 16:01

nilgun


3 Answers

So you do have the below code in your application? If you don't you should add it,might be it is causing the problem.

<bean id="transactionManager"   
class="org.springframework.orm.hibernate3.HibernateTransactionManager">    
<property name="sessionFactory" ref="sessionFactory"/>    
</bean>    
<tx:annotation-driven/>

Remove this property as mentioned below

<property name="current_session_context_class">thread</property>

You are overriding Spring which sets this to SpringSessionContext.class. This is almost certainly at least part of your problem.

Spring manages your session objects. These session objects that it manages are tied to Spring transactions. So the fact that you are getting that error means to me that it is most likely due to how you are handling transactions.

in other words don't do this

Transaction tx = session.beginTransaction();

unless you want to manage the life cycle of the session yourself in which case you need to call session.open() and session.close()

Instead use the framework to handle transactions. I would take advantage of spring aspects and the declarative approach using @Transactional like I described earlier its both cleaner and more simple, but if you want to do it pragmatically you can do that with Spring as well. Follow the example outlined in the reference manual. See the below link:

http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/orm.html#orm-hibernate-tx-programmatic

like image 72
Sheel Avatar answered Nov 09 '22 11:11

Sheel


Above error suggest, you are not able to get the session as session is closed sometimes. You can use openSession() method instead of getCurrentSession() method.

Session session = this.getSessionFactory().openSession();
session.beginTransaction();
// Your Code Here.
 session.close();

Drawback with this approach is you will explicitly need to close the session. In single threaded environment it is slower than getCurrentSession().

Check this Link Also:- Hibernate Session is closed

like image 26
Shipra Garg Avatar answered Nov 09 '22 11:11

Shipra Garg


The problem is that you have a single hibernate session and two data stores. The session is bound to the transaction. If you open a new transaction towards the other database this will effectively open a new session for this database and this entity manager.

this is equivalent to @Transactional(propagation = Propagation.REQUIRES_NEW)

You need to ensure that there are two different transactions/sessions bound to each of the persistent operations towards the two databases.

like image 39
Alexander Petrov Avatar answered Nov 09 '22 10:11

Alexander Petrov