EntityManagerFactory 与 SessionFactory 比较

在本文中,我们探讨了EntityManagerFactory和SessionFactory的设置和使用。我们了解到,两者的主要用途都是为数据库通信创建会话对象。很明显,SessionFactory是 Hibernate 对标准EntityManagerFactory的特定改编。

如果我们需要 Hibernate 功能,那么使用SessionFactory是个不错的选择。但是,为了获得更标准化的方法,我们应该倾向于 JPA 规范,这意味着EntityManagerFactory是更好的选择。

在以下章节中,我们将研究这两个工厂类之间的区别,以便更好地理解何时使用它们。


什么是EntityManagerFactory?
Java持久性 API (JPA)是用于管理 Java 应用程序中持久数据的规范。它提供了一种与关系数据库交互的标准方法。EntityManager作为JPA的核心接口,用于与持久性上下文交互并管理实体的生命周期。它提供了轻量级实例,其中包含用于基本CRUD操作的方法。

话虽如此,我们注意到我们经常需要EntityManager实例,而这正是EntityManagerFactory可以帮助我们的地方。EntityManagerFactory是一个 JPA 接口,它创建EntityManager的实例,从而能够以线程安全的方式与持久性上下文进行交互。

设置过程
首先,让我们先定义一个实体:

@Entity(name = "persons")
public class Person {
    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Integer id;
    private String name;
    private String email;
   
// omitted getters and setters
}

有几种方法可以设置配置,我们将介绍使用persistence.xml文件的方法。首先,我们需要在 resource/META-INF 文件夹中创建一个新文件并定义连接详细信息:

<persistence-unit name="com.baeldung.sfvsemf.persistence_unit" transaction-type="RESOURCE_LOCAL">
    <description>Persistence Unit for SessionFactory vs EntityManagerFactory code example</description>
    <class>com.baeldung.sfvsemf.entity.Person</class>
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <properties>
        <property name=
"hibernate.hbm2ddl.auto" value="update"/>
        <property name=
"hibernate.show_sql" value="true"/>
        <property name=
"hibernate.generate_statistics" value="false"/>
        <property name=
"hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
        <property name=
"jakarta.persistence.jdbc.driver" value="org.h2.Driver"/>
        <property name=
"jakarta.persistence.jdbc.url" value="jdbc:h2:mem:db2;DB_CLOSE_DELAY=-1"/>
        <property name=
"jakarta.persistence.jdbc.user" value="sa"/>
        <property name=
"jakarta.persistence.jdbc.password" value=""/>
    </properties>
</persistence-unit>

请注意,为简单起见,我们在此示例中使用H2 内存数据库,但不限于此。大多数关系数据库的工作方式相同,我们只需确保使用正确的方言和驱动程序类。

使用示例
配置完成后,使用EntityManagerFactory创建EntityManager对象的过程很简单。

如果操作不当,使用EntityManagerFactory可能会有风险,因为创建它的成本很高。换句话说,EntityManagerFactory实例化需要大量资源,因此建议将其创建为Singleton 类。

虽然我们不会详细介绍用法,但我们会通过代码示例介绍基本操作。我们不会创建单例,而是实例化EntityManagerFactory, 创建 EntityManager对象,然后继续使用它进行简单的数据库操作。

让我们看看实际情况是怎样的:

@Test
public void givenEntityManagerFactory_whenPersistAndFind_thenAssertObjectPersisted() {
    EntityManagerFactory entityManagerFactory =
      Persistence.createEntityManagerFactory("com.baeldung.sfvsemf.persistence_unit");
    EntityManager entityManager = entityManagerFactory.createEntityManager();
    try {
        entityManager.getTransaction().begin();
        Person person = new Person(
"John", "johndoe@email.com");
        entityManager.persist(person);
        entityManager.getTransaction().commit();
        Person persistedPerson = entityManager.find(Person.class, person.getId());
        assertEquals(person.getName(), persistedPerson.getName());
        assertEquals(person.getEmail(), persistedPerson.getEmail());
    } catch (Exception ex) {
        entityManager.getTransaction().rollback();
    } finally {
        entityManager.close();
        entityManagerFactory.close();
    }
}

什么是SessionFactory?
流行的ORM框架 Hibernate 使用SessionFactory作为其工厂类来创建和管理Session实例。与EntityManagerFactory一样,SessionFactory也提供了一种线程安全的方法来处理数据库连接和 CRUD 操作。

相比之下,Session与EntityManager类似, 因为它与数据库交互、管理事务并处理实体的完整生命周期。

设置过程
在继续设置过程之前,我们假设我们已经具备配置和使用 Hibernate 的实际知识,因为本文不会进行深入解释。如果没有,请参阅我们的Hibernate 相关文章以了解更多信息。

为了演示SessionFactory 的工作原理,我们使用与上例相同的实体类。Hibernate 主要使用 hibernate.cfg.xml 文件进行配置,让我们将其添加到我们的资源中:

<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">org.h2.Driver</property>
        <property name=
"hibernate.connection.url">jdbc:h2:mem:db2;DB_CLOSE_DELAY=-1</property>
        <property name=
"hibernate.connection.username">sa</property>
        <property name=
"hibernate.connection.password"></property>
        <property name=
"hibernate.dialect">org.hibernate.dialect.H2Dialect</property>
        <property name=
"hibernate.hbm2ddl.auto">update</property>
        <property name=
"hibernate.show_sql">true</property>
        <mapping class=
"com.baeldung.sfvsemf.entity.Person"/>
    </session-factory>
</hibernate-configuration>

 使用示例
在使用SessionFactory之前,需要注意的是,与EntityManagerFactory类似,它的创建成本也很高。一般建议将它们用作单例实例。

配置完SessionFactory之后,让我们检查一下如何使用它来创建Session实例并执行基本的数据库操作:

@Test
void givenSessionFactory_whenPersistAndFind_thenAssertObjectPersisted() {
    SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
    Session session = sessionFactory.openSession();
    Transaction transaction = null;
    try {
        transaction = session.beginTransaction();
        Person person = new Person("John", "johndoe@email.com");
        session.persist(person);
        transaction.commit();
        Person persistedPerson = session.find(Person.class, person.getId());
        assertEquals(person.getName(), persistedPerson.getName());
        assertEquals(person.getEmail(), persistedPerson.getEmail());
    } catch (Exception ex) {
        if (transaction != null) {
            transaction.rollback();
        }
    } finally {
        session.close();
        sessionFactory.close();
    }
}

EntityManagerFactory 与 SessionFactory 的比较
这两个工厂有许多相似之处,并且具有相同的用途。它们的主要作用是提供数据库通信实例。我们应该探索其他相似之处、不同之处以及可以利用它们的用例场景。

主要相似点和不同点
除了创建会话实例的职责外,还有其他的相似之处:

  • 两者都通过CriteriaBuilder和HibernateCriteriaBuilder提供补充查询功能。
  • 它们支持交易,帮助我们维护数据完整性。
  • 我们必须小心管理它们,因为它们是资源密集型的,因此最好实例化一次并重复使用该实例。
  • 它们的线程安全设计允许并发访问。
如果我们查看它们的实现,我们会注意到SessionFactory实际上继承了EntityManagerFactory。但是,两者之间存在一些关键差异。主要差异在于SessionFactory是 Hibernate 特有的概念,而EntityManagerFactory是标准 JPA 接口。

另一个重要的区别是 Hibernate 支持二级缓存。 它在SessionFactory 级别运行,允许缓存的数据在所有会话之间共享。此功能特定于 Hibernate,在 JPA 规范中不可用。

用例比较

  • EntityManagerFactory应该用于构建需要独立于供应商的应用程序,换句话说,我们可以轻松交换底层提供程序(Hibernate、EclipseLink、OpenJpa 等)。
  • 如果我们更喜欢 Hibernate 或它的某些特定功能,例如二级缓存和批量查询,则应该使用SessionFactory。

总之,EntityManagerFactory在各种 JPA 实现中更加灵活和可移植,而SessionFactory与 Hibernate 紧密耦合。