在多线程环境中与 Hibernate 会话交互的合适方式

Suitable way to interact with Hibernate sessions in multi-thread environment

Web 应用程序等多线程环境的上下文,每当需要使用 Hibernate 使用会话时,getcurrentsession() 或 opensession() 获取会话的最佳方法是什么?

这里opensession vs getcurrentsession提到getcurrentsession不能用在网络应用程序中。

虽然 session 不是安全的,但由于这个原因,它不适合使用 getcurrentsession 吗?是否需要手动使用opensesion和commit & close flush?

我需要一个明确的说明。谢谢大家

PS : 我将休眠与 Spring 启动应用程序一起用于测试目的。

如果您的 class 路径中有 spring-boot-autocofigure,它很可能已经为您配置了 EntityManagerFactory。如果您的 class 路径上没有它,那么将 SessionFactory 的 bean 配置转换为 EntityManagerFactory 非常容易。完成后,您只需在 spring bean

中执行以下操作即可获得超级实例
@Service
public class MyBookServiceImpl implements BookService {
  @PersistenceContext
  private EntityManager entityManager;

  @Transactional
  public void doSomeFancyHibernateThing() {
    List<Book> allBooks = entityManager.createQuery( "FROM Book", Book.class )
       .getResultList();
  }
}

此时您可以向服务添加任何方法 class 您需要执行该 bean 完成和使用 EntityManager 实例所需的任何任务。您不需要关心多个线程等,因为 spring 会自动为您提供一个 EntityManager 的实例,可以在该线程中安全使用而无需担心其他线程。

注意我在这里说在那个线程中可以安全使用。不要试图在另一个线程中使用该实例并期望不会出现并发症或问题

如果您认为出于任何原因必须使用 SessionFactory,您可以使用这两种方法中的任何一种,具体取决于 想要管理的生命周期session.

如果您想使用 #getCurrentSession(),重要的是您还要为休眠提供以下配置,以便它将当前 session 查找与 thread-local 变量绑定,确保每个线程给出自己的 session object:

hibernate.current_session_context_class=thread

使用上面的好处是你不需要担心Session的生命周期管理,它会在你的交易结束时自动关闭和释放。

例如:

@Transactional
public void doSomeFancyHibernateThing() {
  Session session = sessionFactory.getCurrentSession();
  // do something here, when the method exits, the session is closed & destroyed.
}

如果您想使用 #openSession(),那么返回的 Session object 的生命周期和生存期由您负责,以确保您正确地处理关闭其资源。也就是说,上面的简单方法变成了这样:

public void doSomeFancyHibernateThing() {
  Session session = sessionFactory.openSession();
  try {
     // do something here
  }
  catch ( Exception e ) {
     // do whatever
  }
  finally {
    if ( session != null && session.isOpen() ) {
      session.close();
    }
  }
}

如您所见,答案顶部的 JPA 等效项与 getCurrentSession() 的使用非常相似,当与那个休眠配置配对时。

这里的最大好处是使用 JPA(顶部的示例)直接删除了对 Hibernate 的所有依赖。如果你发现你 运行 进入一个你需要 Hibernate-specific JPA 没有公开的功能的用例,你仍然可以通过解包 EntityManager 轻松获得 Session ,如下所示:

Session session = entityManager.unwrap( Session.class );
// do whatever you need with a session easily.