24

I need to make sure many concurrent users be able to access the database. Although after each commit I close the session but sometimes my code runs into following error, but when I do the same operation for few times it surpass the error and works.

My hibernate is 4.2.1.Final

Messages:   
nested transactions not supported
File:   org/hibernate/engine/transaction/spi/AbstractTransactionImpl.java
Line number:    152

My Code

session = HibernateUtil.getSession();
session.getTransaction().begin();       OR session.beginTransaction();
       ...   to do ....
session.getTransaction().commit();
session.close();                        OR HibernateUtil.closeSession();

HibernateUtil

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;

public class HibernateUtil {

   private static ServiceRegistry serviceRegistry;
   private static final ThreadLocal<Session> threadLocal = new ThreadLocal();
   private static SessionFactory sessionFactory;
    private static SessionFactory configureSessionFactory() {
        try {

            Configuration configuration = new Configuration();
            configuration.configure();
            serviceRegistry = new ServiceRegistryBuilder()
                                 .applySettings(configuration.getProperties())
                                 .buildServiceRegistry();
            sessionFactory = configuration.buildSessionFactory(serviceRegistry);

            return sessionFactory;
        } catch (HibernateException e) {
            System.out.append("** Exception in SessionFactory **");
            e.printStackTrace();
        }
       return sessionFactory;
  }


  static {
    try {
      sessionFactory = configureSessionFactory();
    } catch (Exception e) {
      System.err.println("%%%% Error Creating SessionFactory %%%%");
      e.printStackTrace();
    }
  }

  private HibernateUtil() {
  }

  public static SessionFactory getSessionFactory() {
    return sessionFactory;
  }

  public static Session getSession() throws HibernateException {
    Session session = threadLocal.get();

    if (session == null || !session.isOpen()) {
      if (sessionFactory == null) {
        rebuildSessionFactory();
      }
      session = (sessionFactory != null) ? sessionFactory.openSession() : null;
      threadLocal.set(session);
    }

    return session;
  }

  public static void rebuildSessionFactory() {
    try {
      sessionFactory = configureSessionFactory();
    } catch (Exception e) {
      System.err.println("%%%% Error Creating SessionFactory %%%%");
      e.printStackTrace();
    }
  }

  public static void closeSession() throws HibernateException {
    Session session = (Session) threadLocal.get();
    threadLocal.set(null);

    if (session != null) {
      session.close();
    }
  }
}

Configuration

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

    <session-factory>

        <!-- Database connection settings -->
        <property name="connection.driver_class">
            com.mysql.jdbc.Driver
        </property>
        <property name="connection.url">
            jdbc:mysql://localhost:3306/MyProject
        </property>
        <property name="connection.username">root</property>
        <property name="connection.password"></property>

        <!-- JDBC connection pool (use the built-in) -->
        <property name="connection.pool_size">12</property>

        <!-- SQL dialect -->
        <property name="dialect">
            org.hibernate.dialect.MySQLDialect
        </property>

        <!-- Enable Hibernate's automatic session context management -->
        <property name="current_session_context_class">thread</property>

        <!-- Disable the second-level cache  -->
        <property name="cache.provider_class">
            org.hibernate.cache.NoCacheProvider
        </property>

        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property>

                <mapping class="com.project.common.Project" />
                <mapping class="com.project.common.School" />
                <mapping class="com.project.common.Address" />
                <mapping class="com.project.common.Female" />
                <mapping class="com.project.common.Male" />
                <mapping class="com.project.common.Credential" />
                <mapping class="com.project.common.Users" />

    </session-factory>

</hibernate-configuration>
  • Looks like a race hazard to me; there are many pitfalls to writing your own `ThreadLocal` scoped transactions. I would strongly advise against writing your own session management code, use one of the [`CurrentSessionContext`](http://docs.jboss.org/hibernate/orm/3.6/javadocs/org/hibernate/context/CurrentSessionContext.html) implementations provided. – Boris the Spider Jul 09 '13 at 07:18
  • do you know of any example? –  Jul 09 '13 at 07:19
  • Maybe start with [the documentation](http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/architecture.html#architecture-current-session). – Boris the Spider Jul 09 '13 at 07:21
  • 2
    have a look at http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/transactions.html#transactions-demarcation-nonmanaged – Tim Norman Jul 22 '13 at 05:37
  • @TimNorman I have already read that, but could not solve the issue. –  Jul 22 '13 at 13:15
  • 1
    Which version of Hibernate, database, and application server are you using? Is there a JTA transacation manager? Your "My code" snippet does not handle exceptions at all. Even if it is successful, `session.close()` does NOT reset the `ThreadLocal` variable, this is different from `HibernateUtils.closeSession()`. Have you checked that [question](http://stackoverflow.com/q/14184703/2390083)? – Beryllium Jul 23 '13 at 18:45
  • @Beryllium question is updated –  Jul 24 '13 at 09:34

5 Answers5

26

In your "My code" snippet, there might be some problems:

  1. In case of an exception, there is no finally block to close the session
  2. You are calling session.close(), but this is different from HibernateUtils.closeSession(). So the ThreadLocal is not cleared.
  3. There is no catch block for exceptions; as a consequence there is no rollback.
  4. Do you rethrow exceptions or are they (silently) ignored?

If there is an exception in the "to do" block after begin(), the transaction remains open, and ThreadLocal is not cleared.

Your code may work fine normally, but under high load there might be (SQL lock) timeouts etc., and in this case, once in a while, an exception will be thrown.

So check each snippet for correct exception handling:

final Session session = HibernateUtil.getSession();
try {
  final Transaction transaction = session.beginTransaction();
  try {
    // The real work is here
    transaction.commit();
  } catch (Exception ex) {
    // Log the exception here
    transaction.rollback();
    throw ex;
  }
} finally {
  HibernatilUtil.closeSession();
}

You could add some "book-keeping" code to HibernateUtil.getSession() and HibernateUtil.closeSession(): Log each access, including the thread's name. Eventually one or multiple "gets" by the same thread must be followed by a "close".

In your case I would even consider to have only one "get", and pass the session around as long as your thread is doing its unit of work: This way it's possibly easier to find the problem.


There is another question on SO which reports a similar problem: Hibernate 4.1.9 (latest final build) reporting `nested transactions not supported.

You could add some code after commit() to check, if the transaction has been really completed (by calling wasCommitted()).

A quote from the Javadoc of wasCommitted():

This method could return false even after successful invocation of commit(). As an example, JTA based strategies no-op on commit() calls if they did not start the transaction; in that case, they also report wasCommitted() as false.

Community
  • 1
  • 1
Beryllium
  • 12,808
  • 10
  • 56
  • 86
  • thanks for your answer, do you think the current settings of mine are enough to handle large number of users? –  Jul 25 '13 at 12:00
  • @Mir Moorido Is your main problem resolved? You haven't selected an answer yet. Anyway it's hard to tell if your settings are suitable for a large number of users. You should set up a test with different numbers of concurrent users. – Beryllium Jul 25 '13 at 15:53
  • I have used it in my code but did not have much time to test it, will test it and let you know thanks –  Jul 25 '13 at 21:27
  • @MirMoorido Thanks for the 2nd bounty, very noble indeed. I am curious about which measure/action helped you out? – Beryllium Aug 06 '13 at 06:41
  • No worries, you deserved it, it does not show the same error anymore and I believe thats because of the correct exception handling and using HibernateUtil.closesession() thanks anyway. –  Aug 06 '13 at 11:06
16

You probably have begun a transaction, and trying to begin another one without having committed or rollbacked the previous one. The idiom when using programmatic transaction demarcation is the following one:

Transaction transaction = null;
    try {
      session = HibernateUtil.getSession();
      transaction  = session.beginTransaction();
       ...   to do ....
      transaction.commit();
    }
    catch (RuntimeException e) {
        transaction.rollback();
        throw e;
    }

Add the following property in your Hibernate.cfg.xml

 <prop key="hibernate.current_session_context_class">thread</prop>
MayurB
  • 3,609
  • 2
  • 21
  • 36
  • Thanks question is upated –  Jul 24 '13 at 10:00
  • I thought I need to, do you know of its pros and cons ? –  Jul 24 '13 at 10:05
  • With Hibernate 4+ this property brings another nightmare:https://stackoverflow.com/questions/18832889/spring-transactions-and-hibernate-current-session-context-class – whatswrong Mar 13 '19 at 11:01
5

Use session.beginTransaction() instead of session.getTransaction().begin() in your code. You need to begin a new unit of work, so beginTransaction will begin a new transaction. So your code will look like:

session = HibernateUtil.getSession();
Transaction transaction = session.beginTransaction();
       ...   to do ....
transaction.commit();

Click Here to get more information about beginTransaction(); method.

I think that will resolve your issue. Please let me know if issue persists.

Ankit Zalani
  • 3,068
  • 5
  • 27
  • 47
0

I was facing same problem , i solved my problem by putting tr.commit(); function after every transaction. It happens only when you start any transaction without closing previous transaction.

session = HibernateUtil.getSession();

session.getTransaction().begin();       OR session.beginTransaction();

...   to do ....

session.getTransaction().commit();

session.close();   OR HibernateUtil.closeSession();

Looks like you are also doing same mistake.

Alok Panday
  • 403
  • 4
  • 10
0

I faced a similar issue : org.hibernate.TransactionException: nested transactions not supported

In my case, I opened a session and tried saving . Without commit, I called another method and called session.beginTransaction(); So it throw the error. In order to avoid, I sent the session object as a method parameter and just called session.save instead of again doing begin. Practically used the session object. It worked for me!

Arpita
  • 107
  • 3