1. 简介

EntityManager 是 Java Persistence API(JPA)的一部分,主要负责实现 JPA 2.0 规范中定义的编程接口和生命周期规则。

它提供了一种访问持久化上下文(Persistence Context)的方式,通过其 API 我们可以操作实体对象的状态。

在本文中,我们将深入探讨 EntityManager 的配置、类型以及常用 API 的使用方式。

2. Maven 依赖

首先,我们需要引入 Hibernate 的核心依赖:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.4.24.Final</version>
</dependency>

根据你使用的数据库类型,还需添加相应的 JDBC 驱动依赖。以 MySQL 为例:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.13</version>
</dependency>

以上依赖均可在 Maven Central 中找到。

3. 配置

为了演示 EntityManager 的使用,我们定义一个 Movie 实体类,对应数据库中的 MOVIE 表。

后续我们将使用 EntityManager 来操作这个实体类。

3.1. 定义实体类

我们使用 @Entity 注解将 Movie 类标记为 JPA 实体,并通过 @Table 指定对应的数据库表名:

@Entity
@Table(name = "MOVIE")
public class Movie {
    
    @Id
    private Long id;

    private String movieName;

    private Integer releaseYear;

    private String language;

    // 标准构造函数、getter、setter 省略
}

3.2. persistence.xml 文件配置

当创建 EntityManagerFactory 时,JPA 实现会在类路径下查找 META-INF/persistence.xml 文件。

该文件中定义了 EntityManager 的配置信息:

<persistence-unit name="com.baeldung.movie_catalog">
    <description>Hibernate EntityManager Demo</description>
    <class>com.baeldung.hibernate.pojo.Movie</class> 
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <properties>
        <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
        <property name="hibernate.hbm2ddl.auto" value="update"/>
        <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
        <property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/moviecatalog"/>
        <property name="javax.persistence.jdbc.user" value="root"/>
        <property name="javax.persistence.jdbc.password" value="root"/>
    </properties>
</persistence-unit>

在这个配置中,我们定义了持久化单元(persistence-unit),并指定了底层数据库的相关信息,包括方言、JDBC 驱动等。Hibernate 通过这些配置与数据库建立连接。

4. 容器管理 vs 应用管理的 EntityManager

EntityManager 主要分为两种类型:容器管理(Container-Managed)应用管理(Application-Managed)

4.1. 容器管理的 EntityManager

在容器(如 Spring 或 Jakarta EE)中,通常由容器自动注入 EntityManager 到业务组件中:

@PersistenceContext
EntityManager entityManager;

在这种情况下,容器负责事务的开启、提交或回滚,同时也负责 EntityManager 的关闭,使用起来更加安全省心。如果你手动调用 close() 方法去关闭一个容器管理的 EntityManager,会抛出 IllegalStateException 异常。

4.2. 应用管理的 EntityManager

应用管理的 EntityManager 需要手动创建和关闭。

首先创建 EntityManagerFactory

EntityManagerFactory emf = Persistence.createEntityManagerFactory("com.baeldung.movie_catalog");

然后调用 createEntityManager() 方法获取 EntityManager 实例:

public static EntityManager getEntityManager() {
    return emf.createEntityManager();
}

由于我们手动管理 EntityManager 的生命周期,因此在使用完毕后必须调用 close() 方法释放资源。

4.3. 线程安全性

EntityManagerFactory 是线程安全的,可以在多线程环境中安全使用:

EntityManagerFactory emf = // 获取工厂实例
EntityManager em = emf.createEntityManager();

EntityManager 本身不是线程安全的,应该在单线程环境中使用。每个线程都应该获取自己的 EntityManager 实例,并在使用完成后关闭它。

对于应用管理的 EntityManager,我们可以很容易地实现线程隔离:

EntityManagerFactory emf = // 获取工厂实例 
EntityManager em = emf.createEntityManager();
// 当前线程使用

而在容器管理的场景中,比如 Spring 中:

@Service
public class MovieService {

    @PersistenceContext // 或者 @Autowired
    private EntityManager entityManager;
    
    // 省略业务代码
}

看起来像是多个线程共享一个 EntityManager,但其实容器会注入一个代理对象(如 Spring 的 SharedEntityManagerCreator),确保每个线程拿到的是独立的 EntityManager 实例。

5. Hibernate 实体操作

EntityManager 提供了一系列 API 用于操作实体对象。

5.1. 持久化实体

使用 persist() 方法将对象纳入持久化上下文:

public void saveMovie() {
    EntityManager em = getEntityManager();
    
    em.getTransaction().begin();
    
    Movie movie = new Movie();
    movie.setId(1L);
    movie.setMovieName("The Godfather");
    movie.setReleaseYear(1972);
    movie.setLanguage("English");

    em.persist(movie);
    em.getTransaction().commit();
}

保存成功后,实体对象进入“持久化”状态。

5.2. 加载实体

使用 find() 方法通过主键查找实体:

public Movie getMovie(Long movieId) {
    EntityManager em = getEntityManager();
    Movie movie = em.find(Movie.class, new Long(movieId));
    em.detach(movie);
    return movie;
}

如果只需要获取实体的引用,可以使用 getReference() 方法,它会返回一个代理对象:

Movie movieRef = em.getReference(Movie.class, new Long(movieId));

5.3. 分离实体

使用 detach() 方法将实体从持久化上下文中移除:

em.detach(movie);

分离后,实体进入“游离”状态。

5.4. 合并实体

对于跨事务的实体更新,可以使用 merge() 方法将游离实体的更改合并到持久化上下文中:

public void mergeMovie() {
    EntityManager em = getEntityManager();
    Movie movie = getMovie(1L);
    em.detach(movie);
    movie.setLanguage("Italian");
    em.getTransaction().begin();
    em.merge(movie);
    em.getTransaction().commit();
}

5.5. 查询实体

可以使用 JPQL 查询实体,通过 getResultList() 获取结果列表:

public List<?> queryForMovies() {
    EntityManager em = getEntityManager();
    List<?> movies = em.createQuery("SELECT movie from Movie movie where movie.language = ?1")
      .setParameter(1, "English")
      .getResultList();
    return movies;
}

如果查询结果只有一个对象,可以使用 getSingleResult()

5.6. 删除实体

使用 remove() 方法删除数据库中的实体:

public void removeMovie() {
    EntityManager em = HibernateOperations.getEntityManager();
    em.getTransaction().begin();
    Movie movie = em.find(Movie.class, new Long(1L));
    em.remove(movie);
    em.getTransaction().commit();
}

⚠️ 注意:删除后实体对象并不会进入“游离”状态,而是从持久化上下文中移除。

6. 总结

本文介绍了 Hibernate 中 EntityManager 的核心概念与使用方式,包括配置、类型、线程安全性以及常用操作方法。

完整代码可参考:GitHub 示例代码


原始标题:Guide to the Hibernate EntityManager