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

时间:2014-02-06 16:45:25

标签: java web-services hibernate jersey jackson

我使用这些技术+ c3p0为数据库处理提供了一个Web服务。它大部分时间都可以正常工作,但由于此错误,我的访问失败率为3-5%(有时甚至是10%)。

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

-Session Factory

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

您将对此主题有所了解,

提前致谢

3 个答案:

答案 0 :(得分:4)

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

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

答案 1 :(得分:3)

您没有正确使用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不是线程安全的。

答案 2 :(得分:2)

似乎已启动事务,并且在事务被提交之前,尝试启动新事务。

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

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

在进行程序化交易管理时,应使用与此类似的习语:

try {
    sess.getTransaction().begin();

    // do some work

    sess.getTransaction().commit()
}
catch (RuntimeException e) {
    sess.getTransaction().rollback();
    throw e;
}

同样重要的是要注意,在回滚后,会话无法重用,因为它处于不一致状态。

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