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 示例代码