Hibernate SessionFactory与JPA EntityManagerFactory的区别

314
我对Hibernate还不太了解,不确定应该使用Hibernate的SessionFactory还是JPA的EntityManagerFactory来创建一个Hibernate的Session。
这两者之间有什么区别?使用它们各自的优缺点是什么?

6
这个重复问题的答案非常好。https://dev59.com/0GAg5IYBdhLWcg3wjrkZ - Sanghyun Lee
8个回答

423

推荐使用JPA标准定义的EntityManagerFactoryEntityManager

SessionFactorySession是特定于Hibernate的。 EntityManager在幕后调用了Hibernate会话。如果您需要一些在EntityManager中不可用的特定功能,可以通过调用以下代码获取会话:

Session session = entityManager.unwrap(Session.class);

2
@elpisu - 我其实不能提供推荐。在过去两年中,我仅使用官方文档作为学习资源,因此不了解其他可靠的资源。但是官方文档已经足够好了。 - Bozho
7
@Bozho,我知道现在有点晚了,但是使用 SessionFactory 和 Session 有哪些缺点?为什么使用 JPA 更受欢迎?谢谢。 使用 SessionFactory 和 Session 的缺点包括:需要手动管理它们的生命周期、不支持持久性上下文的托管实体以及需要手动处理事务。而使用 JPA 可以更方便地管理实体类和数据库之间的映射,自动处理持久化上下文和事务,并且可以使用面向对象的方式进行查询。这使得开发更加高效和可维护,并且减少了代码量。 - manash
12
使用JPA比Hibernate API更优,因为它是Java Enterprise标准。使用JPA(并限制自己不使用Hibernate特定功能)可以提高应用程序的可移植性,即您可以选择切换到不同的持久化框架,并且只需进行最小的应用程序更改,只要该框架符合JPA标准即可。 - László van den Hoek
2
仅仅因为它是企业标准就更好吗?我对此表示怀疑。标准通常演变缓慢且复杂。那么真正的实际好处呢?JPA 更好,因为它有 TypedQuery,可以避免在各个地方进行类型转换。 - Bastian Voigt
2
这种从EntityManager获取Session的方法,和SessionFactory.getCurrentSession()一样吗?我的意思是,如果还没有创建Session,它会打开新的Session吗?在多线程环境下它是如何工作的? - Sarvesh
显示剩余4条评论

81

SessionFactory vs. EntityManagerFactory

如我在Hibernate用户指南中解释的那样,Hibernate的SessionFactory扩展了JPA的EntityManagerFactory,如下图所示:

JPA and Hibernate relationship

因此,SessionFactory也是一个JPA EntityManagerFactory

无论是SessionFactory还是EntityManagerFactory都包含实体映射元数据,并允许您创建一个Hibernate的Session或一个EntityManager

Session vs. EntityManager

SessionFactoryEntityManagerFactory一样,Hibernate的Session也扩展了JPA的EntityManager。因此,Session中定义的所有方法都可在Hibernate中使用。

Session 和`EntityManager实体状态转换成SQL语句,如SELECT、INSERT、UPDATE和DELETE。

Hibernate vs. JPA启动

在启动JPA或Hibernate应用程序时,您有两个选择:

  1. 您可以通过Hibernate原生机制引导,并通过BootstrapServiceRegistryBuilder创建一个SessionFactory。如果您正在使用Spring,则Hibernate引导是通过LocalSessionFactoryBean完成的,例如此GitHub示例
  2. 或者,您可以通过Persistence类或EntityManagerFactoryBuilder创建一个JPA EntityManagerFactory。如果您正在使用Spring,则JPA引导是通过LocalContainerEntityManagerFactoryBean完成的,例如此GitHub示例

通过JPA引导是更好的选择。这是因为JPA的FlushModeType.AUTO比传统的FlushMode.AUTO更好,后者会破坏本机SQL查询的读写一致性

JPA到Hibernate的解封装

而如果您通过JPA引导,并使用@PersistenceUnit注释注入了EntityManagerFactory

@PersistenceUnit
private EntityManagerFactory entityManagerFactory;

您可以使用unwrap方法轻松地访问底层的Sessionfactory

SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);

使用JPA EntityManager也可以做到这一点。如果通过@PersistenceContext注解注入EntityManager:

@PersistenceContext
private EntityManager entityManager;

您可以使用unwrap方法轻松访问底层的Session

Session session = entityManager.unwrap(Session.class);

结论

因此,您应该通过JPA引导程序,使用EntityManagerFactoryEntityManager,并仅在想要访问一些Hibernate特定方法(例如通过其自然标识符获取实体)时,将其解包到相关的Hibernate接口。


32
我希望补充一下,您还可以通过从EntityManager调用getDelegate()方法来获取Hibernate的会话。

例如:


例如:

Session session = (Session) entityManager.getDelegate();

34
根据Java文档,需要优先使用 unwrap() 而不是 getDelegate()。参见 javaee 6javaee 7 - ryenus

25

我更喜欢JPA2 EntityManager API 而不是 SessionFactory,因为它更现代化。一个简单的例子:

JPA:

@PersistenceContext
EntityManager entityManager;

public List<MyEntity> findSomeApples() {
  return entityManager
     .createQuery("from MyEntity where apples=7", MyEntity.class)
     .getResultList();
}

SessionFactory:

@Autowired
SessionFactory sessionFactory;

public List<MyEntity> findSomeApples() {
  Session session = sessionFactory.getCurrentSession();
  List<?> result = session.createQuery("from MyEntity where apples=7")
      .list();
  @SuppressWarnings("unchecked")
  List<MyEntity> resultCasted = (List<MyEntity>) result;
  return resultCasted;
}

我认为第一个看起来更清晰,而且也更容易测试,因为EntityManager可以轻松模拟。


39
如果你愿意的话,可以让任何代码变得复杂。return sessionFactory.getCurrentSession().createQuery("from User where id=1").list() - wst
你是如何直接获取entityManager的?你需要使用sessionfactory来获取session。 :D - Deepu--Java

23
使用EntityManagerFactory方法可以让我们使用回调方法注释,如@PrePersist、@PostPersist、@PreUpdate,无需额外配置。
在使用SessionFactory时使用类似的回调需要额外的努力。
相关Hibernate文档可以在这里这里找到。
相关SOF问题和Spring论坛讨论可以在这里这里找到。

2

EntityManagerFactory是标准实现,不同的ORM都是相同的。如果你将ORM迁移到其他提供商(如EclipseLink),在处理事务时不会有任何变化。与之相反,如果您使用Hibernate的session工厂,则与Hibernate API绑定,无法迁移到新的供应商。


2
通过使用EntityManager,代码不再与Hibernate紧密耦合。但是为了实现这一点,在使用时我们应该使用:

javax.persistence.EntityManager

代替
org.hibernate.ejb.HibernateEntityManager

同样地,对于EntityManagerFactory,请使用javax接口。这样,代码就可以松散耦合。如果有比Hibernate更好的JPA 2实现,切换将会很容易。在极端情况下,我们可以将其强制转换为HibernateEntityManager。

0

EntityManager接口类似于hibernate中的sessionFactory。 javax.persistence包下有EntityManager,而org.hibernate.Session/sessionFactory包下有session和sessionFactory。

Entity manager是JPA特有的,而session/sessionFactory则是hibernate特有的。


你的回答是正确的,但与Sangyun Lee在他的评论中提到的答案本质上相同...所以是重复的。 - RWC
SessionFactory ~= EntityManagerFactory,但不是EntityManager。答案错误。 - Kumar Manish

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