Hibernate + Jersey + Jackson 随机获得“org.hibernate.TransactionException:不支持嵌套事务”

Posted

技术标签:

【中文标题】Hibernate + Jersey + Jackson 随机获得“org.hibernate.TransactionException:不支持嵌套事务”【英文标题】:Hibernate + Jersey + Jackson getting “org.hibernate.TransactionException: nested transactions not supported” randomly 【发布时间】:2014-02-06 16:45:25 【问题描述】:

我已经使用这些技术 + c3p0 构建了一个 Web 服务来处理数据库。它在大多数情况下都可以正常工作,但由于此错误,我有 3-5% 的比例(有时甚至是 10%)的访问失败。

我正在以这种方式使用 Hibernate:

-会话工厂

private static SessionFactory buildSessionFactory() 
    try            
        Configuration configuration = new Configuration();
        configuration.configure();
        serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();        

        // Create the SessionFactory from hibernate.cfg.xml
        return configuration
        .buildSessionFactory(serviceRegistry);
     catch (Throwable ex) 
        System.err.println("Initial SessionFactory creation failed." + ex);
        throw new ExceptionInInitializerError(ex);
    


public static SessionFactory getSessionFactory() 
    //reabrimos la sesion si esta cerrada al liberar los recursos
    if(sessionFactory.isClosed())
    
        System.out.println("Reopen session");
        sessionFactory.openSession();
    

    return sessionFactory;

然后在我的hibernate.cfg.xml 我有以下行:

<property name="current_session_context_class">thread</property>

最后在我的端点中我定义了一个hibernate_session 类,我使用如下:

@Path("/projects")
public class ProjectServiceImpl 

@Context
SecurityContext security;
Session hibernate_session = null;

@POST
@Path("sync.json")
@Produces(value = "application/json",
        "application/vnd.myapp-v1+json",
        "application/vnd.myapp-v2+json")
public Response syncProjects(
        @DefaultValue("") @FormParam("projects") String in_projects_str,
        @DefaultValue("0") @FormParam("last_sync") long last_sync,
        @Context Request request) 

   //...

   hibernate_session = HibernateUtil.getSessionFactory()
            .getCurrentSession();

  if (hibernate_session == null) 
    ResponseMessage rm = new ResponseMessage();
        rm.setCode(Status.INTERNAL_SERVER_ERROR.getStatusCode());
        rm.setMessage("Hibernate Session is Null");
        rm.setType("ERROR");
        return Response.status(Status.INTERNAL_SERVER_ERROR).entity(rm)
                .type("application/json").build();
    

    try 

        hibernate_session.beginTransaction();

        //Database work...

        hibernate_session.flush();

        hibernate_session.getTransaction().commit();

        catch (RuntimeException | IllegalAccessException
                | InvocationTargetException e) 
            try 
                if (hibernate_session.getTransaction() != null) 
                    hibernate_session.getTransaction().rollback();
                
             catch (RuntimeException rbe) 
                System.err.println("Couldn’t roll back transaction");
            

            e.printStackTrace();
            ResponseMessage rm = new ResponseMessage();
            rm.setCode(Status.INTERNAL_SERVER_ERROR.getStatusCode());
            rm.setMessage(e.getMessage());
            rm.setType("ERROR");
            return Response.status(Status.INTERNAL_SERVER_ERROR).entity(rm)
                    .type("application/json").build();

        
    

    return Response.ok().entity(result_entity)
            .type("application/json").build();

我的hibernate_session 是一个类属性,我必须将其更改为局部变量吗?据我所知,端点将在不同的线程中执行,因此我假设我正在使用端点容器类的不同实例,并且这些类属性不会被多个请求覆盖。

我们将不胜感激您对此主题的任何启发,

提前致谢

【问题讨论】:

检查您是否没有在//Database work... 部分的某处开始另一个事务 有类级别的注解吗? 我添加了类级别注释@stevedbrown 也许会有完整的堆栈跟踪,我们可以更好地诊断您的问题。 【参考方案1】:

感谢大家的回复。我终于设法解决了这个问题。

在我的多个条目之一中,有一个开始事务(创建条件所必需的)但没有提交。结果是之前调用过该方法的重用线程会抛出嵌套异常。通过提交事务,问题解决了:)

【讨论】:

【参考方案2】:

您没有正确使用 openSession 和 getCurrentSession。

public static SessionFactory getSessionFactory() 
    //reabrimos la sesion si esta cerrada al liberar los recursos
    //change this: if(sessionFactory.isClosed()) to this:
    if(sessionFactory == null || sessionFactory.isClosed())
    
        System.out.println("Reopen session"); // Really setup session factory
        //change this: sessionFactory.openSession(); to this:
        sessionFactory = buildSessionFactory();
    

    return sessionFactory;

不过,这不是问题所在,您的代码并没有按照应有的方式进行。你需要改变:

hibernate_session = HibernateUtil.getSessionFactory().getCurrentSession();

hibernate_session = HibernateUtil.getSessionFactory().openSession();

根据 SessionFactory Javadoc:

获取当前会话。确切的“当前”含义的定义由配置为使用的 CurrentSessionContext impl 控制。

假设您的 CurrentSessionContext 不是线程安全的是安全的。

【讨论】:

AFAIK getCurrentSession() 会返回一个链接到当前线程的有效会话,正如hibernate.xml.cfg 所说。但我会改变它并试一试。如果我使用openSession(),我必须在方法结束时调用closeSession(),对吗? 我很确定您看到 getCurrentSession() 共享您的会话 - 您可以通过使用基于注释的 Spring 事务管理器更优雅地解决这个问题。在这种情况下,您无需担心任何事务管理,getCurrentSession 将起作用。如果您使用事务范围的会话,则提交事务将关闭会话。【参考方案3】:

似乎启动了一个事务,并且在事务提交之前尝试启动一个新事务。

这解释了不支持嵌套事务(正在进行的事务中的第二个事务)的错误消息。

这可能是由于错误处理不正确造成的,例如启动事务,未捕获异常或捕获并忽略,然后尝试在未完成提交或回滚的情况下开始第二个事务。

在进行程序化事务管理时应该使用类似于此的成语:

try 
    sess.getTransaction().begin();

    // do some work

    sess.getTransaction().commit()

catch (RuntimeException e) 
    sess.getTransaction().rollback();
    throw e;

同样重要的是要记住,回滚后会话不能被重用,因为它处于不一致的状态。

如果使用像 Spring 这样的框架,使用注解 @Transactional 进行声明式事务管理可以为我们解决大部分这些问题并导致代码更易于维护,EJB3 也具有类似的功能。

【讨论】:

谢谢你,我开始看到春天了。您知道用户请求中的线程是否被重用?还是每个请求都有一个新线程? 每个请求从开始到结束都绑定到一个线程,并且在调用数据库时,servlet 线程阻塞然后恢复,直到请求完成。在请求结束时,根据容器实现,线程将进入线程池,在下一个请求中它们会被重用,这是一种优化。

以上是关于Hibernate + Jersey + Jackson 随机获得“org.hibernate.TransactionException:不支持嵌套事务”的主要内容,如果未能解决你的问题,请参考以下文章

用Jersey构建RESTful服务5--Jersey+MySQL5.6+Hibernate4.3

用Jersey构建RESTful服务7--Jersey+SQLServer+Hibernate4.3+Spring3.2

markdown 使用Hibernate和MariaDB开发Jersey(Restful Web)应用程序

用Jersey+spring+hibernate构建RESTful服务

Jersey API + JPA/Hibernate Criteria延迟加载不起作用

如何使用 Jax-RS(Jersey) 在 Tomcat7 上运行应用程序 Hibernate 5.x、Jpa 2.1、Java EE7(javaee-api 7.0)