2013-07-09 11 views
20

Muszę się upewnić, że wielu równoczesnych użytkowników ma dostęp do bazy danych. Chociaż po każdym zatwierdzeniu zamykam sesję, ale czasami mój kod przechodzi do następującego błędu, ale gdy wykonuję tę samą operację kilka razy, to przekracza ona błąd i działa.Jak uniknąć transakcji zagnieżdżonych nieobsługiwanego błędu?

Moja hibernacji jest 4.2.1.Final

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

mój kod

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

Konfiguracja

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

Wygląda na to, że jest to dla mnie zagrożenie rasowe; istnieje wiele pułapek na pisanie własnych transakcji "ThreadLocal". Zdecydowanie odradzam pisanie własnego kodu zarządzania sesją, używaj jednego z ['CurrentSessionContext'] (http://docs.jboss.org/hibernate/orm/3.6/javadocs/org/hibernate/context/CurrentSessionContext.html) wdrożone wdrożenia. –

+0

czy znasz jakiś przykład? –

+0

Może zacznij od [dokumentacji] (http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/architecture.html#architecture- current_session). –

Odpowiedz

25

W swoim „My” fragmentu kodu, nie mogą być pewne problemy :

  1. w przypadku wyjątek nie ma finally blok, aby zamknąć sesję
  2. Jesteś Calli ng session.close(), ale różni się to od HibernateUtils.closeSession(). Tak więc ThreadLocal nie jest wyczyszczone.
  3. Nie ma bloku dla wyjątków; w konsekwencji nie ma żadnego rollback.
  4. Czy odrzucasz wyjątki, czy też są one (w milczeniu) ignorowane?

Jeśli istnieje wyjątek w „do zrobienia” blok po begin(), transakcja pozostaje otwarta, a ThreadLocal nie rozjaśnia.

Twój kod może działać poprawnie, ale przy dużym obciążeniu mogą występować limity czasu (blokady SQL) itp., W takim przypadku raz na jakiś czas zostanie zgłoszony wyjątek.

więc sprawdzić każdy fragment dla prawidłowego obchodzenia wyjątek:

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

Można dodać trochę „Księgowość” kod do HibernateUtil.getSession() i HibernateUtil.closeSession(): Zaloguj każdy dostępu, w tym nazwy wątku . Ostatecznie po jednym lub wielu "dostaje" przez ten sam wątek musi następować "zamknij".

W twoim przypadku rozważałbym nawet, aby mieć tylko jedno "dostać", i przekazać sesję tak długo, jak twój wątek wykonuje swoją jednostkę pracy: W ten sposób prawdopodobnie łatwiej jest znaleźć problem.


Jest jeszcze jedno pytanie na SO, które zgłasza podobny problem: Hibernate 4.1.9 (latest final build) reporting `nested transactions not supported.

Możesz dodać kod po commit(), aby sprawdzić, czy transakcja została rzeczywiście zakończona (dzwoniąc pod numer wasCommitted()).

cytat z Javadoc z wasCommitted():

Metoda ta mogłaby return false nawet po pomyślnym wywołaniu commit(). Na przykład strategie oparte na JTA no-op on commit(), jeśli nie rozpoczęły transakcji; w takim przypadku zgłaszają również wartość "Zaimek"() jako "fałsz".

+1

Dzięki temu pytaniu jest zaktualizowane, –

+0

dzięki za odpowiedź, czy uważasz, że obecne ustawienia są wystarczające do obsługi dużej liczby użytkowników? –

+0

@ Moir Moorido Czy Twój główny problem został rozwiązany? Nie wybrałeś jeszcze odpowiedzi. W każdym razie trudno powiedzieć, czy twoje ustawienia są odpowiednie dla dużej liczby użytkowników. Powinieneś skonfigurować test z różną liczbą równoczesnych użytkowników. – Beryllium

5

Zastosowanie session.beginTransaction() zamiast session.getTransaction(). Begin() w kodzie. Musisz rozpocząć nową jednostkę pracy, więc beginTransaction rozpocznie nową transakcję. Tak Twój kod będzie wyglądać następująco:

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

Click Here, aby uzyskać więcej informacji o beginTransaction(); metoda.

Myślę, że to rozwiąże twój problem. Daj mi znać, jeśli problem nadal występuje.

+1

Dzięki pytanie jest aktualizowany –

13

Prawdopodobnie rozpoczęto transakcję i próbujesz rozpocząć inną bez uprzedniego zatwierdzenia lub wycofania poprzedniej. Idiom przy użyciu programowego rozgraniczenie transakcji jest następujący:

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

Dodaj następującą właściwość w hibernate.cfg.xml

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

Dzięki pytanie jest aktualizowany –

+0

Czy istnieje potrzeba wątku lokalnego ....? – MayurB

+0

Myślałem, że muszę, czy znasz jej zalety i wady? –

Powiązane problemy