如何避免嵌套事务不支持错误?

问题描述 投票:0回答:6

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

我的休眠是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>
hibernate jakarta-ee transactions
6个回答
26
投票

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

  1. 如果发生异常,没有
    finally
    块来关闭会话
  2. 您正在呼叫
    session.close()
    ,但这与
    HibernateUtils.closeSession()
    不同。所以
    ThreadLocal
    没有被清除。
  3. 没有
    catch
    异常块;结果就没有
    rollback
  4. 您会重新抛出异常还是(默默地)忽略它们?

如果

begin()
之后的“待办事项”区块出现异常,则交易保持打开状态,并且
ThreadLocal
不会被清除。

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

因此检查each片段是否有正确异常处理:

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()
)。

引用

wasCommitted()

 的 Javadoc:

即使成功调用 commit(),此方法也可能返回 false。例如,基于 JTA 的策略如果未启动事务,则不会对 commit() 调用执行任何操作;在这种情况下,他们还会将 wasComfilled() 报告为 false。


16
投票
您可能已经开始一项事务,并尝试在没有提交或回滚前一项事务的情况下开始另一项事务。使用编程式事务划分时的习惯用法如下:

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>
    

6
投票
在代码中使用 session.beginTransaction() 而不是 session.getTransaction().begin() 。您需要开始一个新的工作单元,因此 beginTransaction 将开始一个新的事务。 所以你的代码将如下所示:

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

点击

这里获取有关beginTransaction()的更多信息;方法。

我认为这会解决您的问题。如果问题仍然存在,请告诉我。


0
投票
我面临同样的问题,我通过放置 tr.commit(); 解决了我的问题每次交易后功能。 仅当您开始任何交易而不关闭之前的交易时才会发生这种情况。

session = HibernateUtil.getSession(); session.getTransaction().begin(); OR session.beginTransaction(); ... to do .... session.getTransaction().commit(); session.close(); OR HibernateUtil.closeSession();

看来你也犯了同样的错误。


0
投票
我遇到了类似的问题: org.hibernate.TransactionException:不支持嵌套事务

就我而言,我打开了一个会话并尝试保存。没有提交,我调用了另一个方法并调用了 session.beginTransaction(); 所以它抛出错误。 为了避免,我将会话对象作为方法参数发送,并只调用 session.save 而不是再次执行 begin 。实际使用了session对象。 这对我有用!


0
投票
因此,我们可以使用 session.getTransaction().begin() 代替 transaction.begin() 并确保 session 和 sessionfactory 在finally块中关闭

© www.soinside.com 2019 - 2024. All rights reserved.