控制Hibernate会话(何时手动关闭)

72

我是Hibernate的新手,在阅读了Hibernate API和教程之后,似乎在不使用会话时应该关闭会话。

类似于这样:

Session sess=getSession();
Transcration tx=sess.beginTranscration();
//do something using teh session
sess.save(obj);
tx.commit();
sess.close;

在独立应用程序中使用时我没有问题。 但是在Web应用程序中使用时我不确定。

例如,我有一个servlet:TestServlet 用于接收来自客户端的参数,然后我调用一个Manager根据这些参数查询一些内容,就像这样:

class TestServlet{
  doGet(HttpServletRequset,httpServletResponse){
    String para1=request.getParam...();
    String para2=.....
    new Manager().query(para1,para2);
  }
}

class Manager{
  public String query(String pa1,String pa2){
    Session=....// get the session
    //do query using para1 and 1
    session.close() //Here, I wonder if I should close it.
  }
}

我应该在查询方法中关闭session吗?

因为有人告诉我,Hibernate中的session就像JDBC中的connection一样。所以频繁地打开和关闭它是正确的方式吗?

顺便问一下,每次都需要tx.commit()吗?

还有,在Servlet中使用session时,会出现什么线程问题,因为我看到API中session不是线程安全的。

4个回答

128

我是hibernate的新手,在阅读了hibernate api和教程之后,似乎在不使用时应该关闭session。

当你完成操作时应该关闭它(但是我们将会看到,这可以自动完成)。

在独立应用程序中使用时没有问题。但是在Web应用程序中使用时就不确定了。

好吧,正如文档11.1.1.工作单元中所解释的那样,在多用户客户端/服务器应用程序中最常见的模式是每个请求一个session

例如,我有一个servlet:TestServlet 用于接收来自客户端的参数,然后调用Manager根据参数查询一些内容:就像这样(...)。我应该在查询方法中关闭session吗?

这完全取决于您如何获取session。

  • 如果您使用sessionFactory.getCurrentSession(),则将获得绑定到事务生命周期的“当前session”,并且在事务结束时(提交或回滚)将自动刷新和关闭。
  • 如果您决定使用sessionFactory.openSession(),则必须自己管理session,并手动刷新和关闭它。

要实现一个每个请求一个会话的模式,建议使用第一种方法(更简单、更简洁)。使用第二种方法来实现长时间对话

维基页面Sessions and transactions是这个主题文档的很好补充。

顺便问一下,每次都需要tx.commit()吗?

你可能想阅读Non-transactional data access and the auto-commit mode以澄清一些事情,但简而言之,你的Hibernate代码必须在事务内执行,并且我建议使用显式的事务边界(即显式的beginTransactioncommit)。

此外,在Servlet中使用session存在什么线程问题,因为我看到api中session不是线程安全的。

只要不将其作为Servlet的实例变量,就不会有任何问题。

参考资料


谢谢您的回复。现在我决定创建一个静态类HibernateUtil,它可以通过getCurrentSession()返回会话,然后我在其他地方调用HibernateUtil来获取会话。这样做对吗? - hguser
此外,每个请求都有一个会话,我想知道这是否会导致性能下降,因为当我没有使用Hibernate时,我会使用ConnectionPool。 - hguser
6
如果您没有使用EJB或Spring,那么是的,可以使用一个HibernateUtils类。获取Session并不是一项耗费昂贵的操作,也不会影响性能,它不比从连接池中获取连接更差。 - Pascal Thivent
所以,getCurrentSession() 将会被“session context”实现自动关闭。 - lovespring
@lovespring 我也是这样理解的。@PascalThivent,“一切取决于您如何获取会话”下的两个要点非常重要 - 考虑将其移动到您答案的顶部。几天前我匆忙搜索时,在您的答案中找不到我正在寻找的直接信息;我错过了它。 - Don Cheadle
显示剩余2条评论

2

如果您通过sessionFactory.openSession()获取会话,则必须在外部关闭它。未经意地打开会话的时间过长可能会导致数据泄露。此外,它可能会招来Web应用程序安全威胁。


1

每当第一次调用getCurrentSession()时,会打开一个会话,并在事务结束时关闭。

如果不存在会话,则创建全新的会话;如果已存在会话,则使用现有的会话。它自动配置了自动刷新和自动关闭属性,true表示Session将自动刷新和关闭。

如果您正在使用getCurrentSession(),则无需关闭连接,否则将面临异常。


1
我们可以利用 ThreadLocal
public class MyUtil {
private static SessionFactory sessionFactory;
private static ServiceRegistry serviceRegistry;
private static final ThreadLocal<Session> threadLocal;

static {
try {
    Configuration configuration = new Configuration();
    configuration.configure();
    serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
    sessionFactory = configuration.buildSessionFactory(serviceRegistry);
    threadLocal = new ThreadLocal<Session>();

    } catch(Throwable t){
      t.printStackTrace();
      throw new ExceptionInInitializerError(t);
    }
}
public static Session getSession() {
    Session session = threadLocal.get();
    if(session == null){
        session = sessionFactory.openSession();
        threadLocal.set(session);
    }
    return session;
}

public static void closeSession() {
    Session session = threadLocal.get();
    if(session != null){
    session.close();
    threadLocal.set(null);
    }
}

public static void closeSessionFactory() {
    sessionFactory.close();
    StandardServiceRegistryBuilder.destroy(serviceRegistry);
   }
}

在这里,SessionFactory 只会在静态块中初始化一次。因此,每当 main 类调用 getSession() 时,首先检查 threadLocal 对象中是否存在 Session 对象。 因此,该程序提供了线程安全性。 在每个操作之后,closeSession() 将关闭会话并将 threadLocal 对象设置为 null。 最后调用 closeSessionFactory()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接