如何避免嵌套事务不受支持错误? [英] How to avoid nested transactions not supported error?

查看:19
本文介绍了如何避免嵌套事务不受支持错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要确保许多并发用户能够访问数据库.虽然每次提交后我都会关闭会话,但有时我的代码会遇到以下错误,但是当我多次执行相同的操作时,它会超过错误并起作用.

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.

我的休眠状态是 4.2.1.Final

My hibernate is 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>

推荐答案

在您的我的代码"片段中,可能存在一些问题:

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

  1. 万一出现异常,没有finally块来关闭会话
  2. 您正在调用 session.close(),但这与 HibernateUtils.closeSession() 不同.所以 ThreadLocal 没有被清除.
  3. 没有用于异常的 catch 块;因此没有rollback.
  4. 您是重新抛出异常还是(悄悄地)忽略它们?
  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?

如果begin()之后的to do"块出现异常,事务保持打开状态,ThreadLocal不会被清除.

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

您的代码可能正常工作,但在高负载下可能会出现(SQL 锁)超时等情况,在这种情况下,偶尔会抛出异常.

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

<小时>

您可以向 HibernateUtil.getSession()HibernateUtil.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".

在您的情况下,我什至会考虑只有一个get",并且只要您的线程正在执行其工作单元就可以传递会话:这样可能更容易找到问题.

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.

SO 上还有另一个问题报告了类似的问题:Hibernate 4.1.9(最新最终版本)报告不支持嵌套事务.

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

您可以在commit()之后添加一些代码来检查事务是否真的完成了(通过调用wasCommitted()).

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

引用自 wasCommitted() 的 Javadoc:

A quote from the Javadoc of wasCommitted():

即使在成功调用 commit() 之后,该方法也可能返回 false.例如,基于 JTA 的策略在 commit() 调用上没有操作,如果它们没有启动事务;在这种情况下,他们还将 wasCommitted() 报告为 false.

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.

这篇关于如何避免嵌套事务不受支持错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆