2013-07-09 3 views
20

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

Мой Гибернация 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'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> 
+0

Похоже на раса для меня; есть много подводных камней для написания собственных транзакций с областью действия «ThreadLocal». Я настоятельно рекомендую не писать собственный код управления сеансом, использовать один из ['CurrentSessionContext'] (http://docs.jboss.org/hibernate/orm/3.6/javadocs/org/hibernate/context/CurrentSessionContext.html) реализаций. –

+0

Вы знаете какой-нибудь пример? –

+0

Возможно, начните с [документации] (http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/architecture.html#architecture-current-session). –

ответ

25

В разделе «Мой код» сниппет, там может быть некоторыми проблемы :

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

Если есть исключение в блоке «делать» после 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 (latest final build) reporting `nested transactions not supported.

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

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

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

+1

Спасибо, вопрос уточнен, –

+0

спасибо за ваш ответ, как вы думаете, текущих настроек моего достаточно для обработки большого количества пользователей? –

+0

@Mir Moorido Является ли ваша основная проблема решена? Вы еще не выбрали ответ. В любом случае трудно определить, подходят ли ваши настройки для большого числа пользователей. Вы должны настроить тест с разными количествами одновременных пользователей. – Beryllium

5

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

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

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

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

+1

Спасибо, вопрос уточнен –

13

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

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> 
+0

Спасибо, вопрос встал –

+0

Есть ли необходимость в потоке местного .... ?? – MayurB

+0

Я думал, что мне нужно, вы знаете о его плюсах и минусах? –

Смежные вопросы