web-dev-qa-db-fra.com

Comment éviter les erreurs imbriquées dans les transactions imbriquées?

Je dois m'assurer que de nombreux utilisateurs simultanés puissent accéder à la base de données. Bien que, après chaque validation, je ferme la session mais parfois mon code rencontre l'erreur suivante, mais lorsque je fais la même opération plusieurs fois, l'erreur est dépassée et fonctionne.

Mon hibernation est 4.2.1.Final

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

Mon 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>
21
user2071377

Dans votre extrait de code "Mon code", il peut y avoir des problèmes:

  1. En cas d'exception, il n'y a pas de bloc finally pour fermer la session.
  2. Vous appelez session.close(), mais cela diffère de HibernateUtils.closeSession(). Donc, la ThreadLocal n'est pas effacée.
  3. Il n'y a pas de bloc catch pour les exceptions; en conséquence, il n'y a pas rollback.
  4. Renouvelez-vous les exceptions ou sont-elles (silencieusement) ignorées?

S'il existe une exception dans le bloc "à faire" après begin(), la transaction reste ouverte et ThreadLocal n'est pas effacé.

Votre code peut fonctionner correctement normalement, mais sous une charge élevée, il peut y avoir des délais d'attente (verrouillage SQL), etc., et dans ce cas, une exception est occasionnelle.

Vérifiez donc chaque extrait pour corriger le traitement des exceptions

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

Vous pouvez ajouter du code "comptable" à HibernateUtil.getSession() et HibernateUtil.closeSession(): enregistrez chaque accès, y compris le nom du fil de discussion. Finalement, un ou plusieurs "obtient" par le même fil doivent être suivis d'un "fermer".

Dans votre cas, j’envisagerais même d’avoir un seul "get" et de passer la session aussi longtemps que votre thread accomplit son unité de travail: De cette façon, il est probablement plus facile de trouver le problème.


Une autre question sur SO signale un problème similaire: Hibernate 4.1.9 (dernière version finale) signalant `les transactions imbriquées non prises en charge .

Vous pouvez ajouter du code après commit() pour vérifier si la transaction est vraiment terminée (en appelant wasCommitted()).

Une citation de la Javadoc de wasCommitted():

Cette méthode peut retourner false même après un appel réussi de commit (). À titre d'exemple, les stratégies basées sur JTA ne fonctionnent pas sur les appels de commit () si elles n'ont pas démarré la transaction; dans ce cas, ils signalent également wasCommitted () comme faux. 

26
Beryllium

Vous avez probablement déjà commencé une transaction et essayé d’en commencer une autre sans avoir validé ou annulé la précédente. L'idiome utilisé lors de l'utilisation de la démarcation programmatique de transaction est le suivant:

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

Ajoutez la propriété suivante dans votre fichier Hibernate.cfg.xml

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

Utilisez session.beginTransaction () au lieu de session.getTransaction (). Begin () dans votre code. Vous devez commencer une nouvelle unité de travail, donc beginTransaction commencera une nouvelle transaction . Votre code ressemblera à ceci:

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

Cliquez sur ici pour obtenir plus d'informations sur beginTransaction (); méthode.

Je pense que cela résoudra votre problème. S'il vous plaît laissez-moi savoir si le problème persiste.

5
Ankit Zalani

Je faisais face au même problème, j'ai résolu mon problème en mettant tr.commit (); fonction après chaque transaction ..___ Cela ne se produit que lorsque vous démarrez une transaction sans fermer la transaction précédente.

session = HibernateUtil.getSession();

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

...   to do ....

session.getTransaction().commit();

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

On dirait que vous faites aussi la même erreur.

0
Alok Panday

J'ai rencontré un problème similaire: Org.hibernate.TransactionException: transactions imbriquées non prises en charge

Dans mon cas, j'ai ouvert une session et essayé de sauvegarder. Sans commit, j'ai appelé une autre méthode et appelé session.beginTransaction (); Ainsi, il renvoie l'erreur . Afin d'éviter, j'ai envoyé l'objet de session en tant que paramètre de méthode et appelé simplement session.save au lieu de nouveau faire commence. Pratiquement utilisé l'objet de session . Cela a fonctionné pour moi!

0
Arpita