Подтвердить что ты не робот

Как избежать вложенных транзакций не поддерживается ошибка?

Мне нужно убедиться, что многие одновременные пользователи смогут получить доступ к базе данных. Хотя после каждой фиксации я закрываю сессию, но иногда мой код запускается в следующую ошибку, но когда я делаю одну и ту же операцию несколько раз, она превосходит ошибку и работает.

Мой спящий режим - 4.2.1. Final

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

Мой код

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();
    }
  }
}

Конфигурация

<?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 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>
4b9b3361

Ответ 1

В вашем фрагменте "Мой код" могут возникнуть некоторые проблемы:

  • В случае исключения не существует блока finally для закрытия сеанса
  • Вы вызываете session.close(), но это отличается от HibernateUtils.closeSession(). Таким образом, ThreadLocal не очищается.
  • Для исключений нет блока catch; как следствие, нет rollback.
  • Вы отказываетесь от исключений или игнорируете их (молча)?

Если в блоке "делать" есть исключение после begin(), транзакция остается открытой, а ThreadLocal не очищается.

Ваш код может нормально работать, но при большой нагрузке могут быть (SQL lock) тайм-ауты и т.д., и в этом случае время от времени будет вызываться исключение.

Поэтому проверяйте каждый фрагмент для правильной обработки исключений:

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();
}

Вы можете добавить некоторый "бухгалтерский" код в HibernateUtil.getSession() и HibernateUtil.closeSession(): регистрировать каждый доступ, включая имя потока. В конце концов один или несколько "попаданий" одного потока должны сопровождаться "закрытием".

В вашем случае я бы даже подумал, что у меня есть только один "get", и пропустите сеанс до тех пор, пока ваш поток выполняет свою работу: таким образом, возможно, легче найти проблему.


Есть еще один вопрос о SO, который сообщает о подобной проблеме: Hibernate 4.1.9 (последняя окончательная сборка), сообщающая, что вложенные транзакции не поддерживаются.

Вы можете добавить код после commit(), чтобы проверить, действительно ли транзакция завершена (путем вызова wasCommitted()).

Цитата из Javadoc wasCommitted():

Этот метод может возвращать false даже после успешного вызова commit(). В качестве примера, стратегии JTA, основанные на принципах no-op, на commit(), если они не начали транзакцию; в этом случае они также сообщают wasCommitted() как false.

Ответ 2

Вероятно, вы начали транзакцию и пытаетесь начать с другого, не совершив или отменив предыдущий. Идиома при использовании программной демаркации транзакций следующая:

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

Добавьте в свой Hibernate.cfg.xml следующее свойство

 <prop key="hibernate.current_session_context_class">thread</prop>

Ответ 3

Используйте session.beginTransaction() вместо session.getTransaction(). begin() в вашем коде. Вам нужно начать новую единицу работы, поэтому beginTransaction начнет новую транзакцию. Таким образом, ваш код будет выглядеть так:

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

Нажмите Здесь, чтобы получить дополнительную информацию о beginTransaction(); Метод.

Я думаю, что это решит вашу проблему. Пожалуйста, дайте мне знать, если проблема сохраняется.