Guide to the Hibernate EntityManager – Hibernate EntityManager指南

最后修改: 2018年 12月 13日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

EntityManager is part of the Java Persistence API. Chiefly, it implements the programming interfaces and lifecycle rules defined by the JPA 2.0 specification.

EntityManager是Java Persistence API的一部分。首先,它实现了JPA 2.0规范所定义的编程接口和生命周期规则。

Moreover, we can access the Persistence Context by using the APIs in EntityManager.

此外,我们可以通过使用EntityManager中的API来访问Persistence Context。

In this tutorial, we’ll take a look at the configuration, types, and various APIs of the EntityManager.

在本教程中,我们将看一下EntityManager的配置、类型和各种API。

2. Maven Dependencies

2.Maven的依赖性

First, we need to include the dependencies of Hibernate:

首先,我们需要包括Hibernate的依赖关系。

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

Depending on the database we’re using, we’ll also have to include the driver dependencies:

根据我们所使用的数据库,我们还必须包括驱动程序的依赖性。

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

The hibernate-core and mysql-connector-java dependencies are available on Maven Central.

hibernate-coremysql-connector-java依赖项可在Maven中心获得。

3. Configuration

3.配置

Now let’s demonstrate the EntityManager by using a Movie entity that corresponds to a MOVIE table in the database.

现在让我们通过使用一个Movie实体来演示EntityManager,该实体与数据库中的MOVIE表相对应。

Over the course of this article, we’ll make use of the EntityManager API to work with the Movie objects in the database.

在本文中,我们将利用EntityManager API来处理数据库中的Movie对象。

3.1. Defining the Entity

3.1.定义实体

Let’s start by creating the entity corresponding to the MOVIE table using the @Entity annotation:

让我们首先使用@Entity注解创建与MOVIE表对应的实体。

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

    private String movieName;

    private Integer releaseYear;

    private String language;

    // standard constructor, getters, setters
}

3.2. The persistence.xml File

3.2.持久性.xml文件

When the EntityManagerFactory is created, the persistence implementation searches for the META-INF/persistence.xml file in the classpath.

当创建 EntityManagerFactory 时,持久化实现会搜索 classpath 中的 META-INF/persistence.xml 文件

This file contains the configuration for the EntityManager:

该文件包含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>

As we can see, we define the persistence-unit that specifies the underlying datastore managed by the EntityManager.

正如我们所见,我们定义了持久化单元,该单元指定了由EntityManager管理的底层数据存储。

Furthermore, we define the dialect and the other JDBC properties of the underlying datastore. Hibernate is database-agnostic. Based on these properties, Hibernate connects with the underlying database.

此外,我们还定义了底层数据存储的方言和其他JDBC属性。Hibernate是数据库无关的。基于这些属性,Hibernate与底层数据库进行连接。

4. Container and Application Managed EntityManager

4.容器和应用程序管理的EntityManager

Basically, there are two types of EntityManager: Container-Managed and Application-Managed.

基本上,有两种类型的EntityManager。容器管理型和应用管理型。

Let’s have a closer look at each type.

让我们仔细看看每种类型。

4.1. Container-Managed EntityManager

4.1.容器管理的EntityManager

Here, the container injects the EntityManager in our enterprise components.

在这里,容器将EntityManager注入我们的企业组件中。

In other words, the container creates the EntityManager from the EntityManagerFactory for us:

换句话说,容器从EntityManagerFactory为我们创建EntityManager

@PersistenceContext
EntityManager entityManager;

This also means the container is in charge of beginning the transaction, as well as committing or rolling it back.

这也意味着容器负责开始交易,以及提交或回滚交易

Similarly, the container is responsible for closing the EntityManager, so it’s safe to use without manual cleanups. Even if we try to close a container-managed EntityManager, it should throw an IllegalStateException.

同样地,容器负责关闭EntityManager,所以它可以安全地使用,而无需手动清理。即使我们试图关闭容器管理的EntityManager,它也应该抛出IllegalStateException.

4.2. Application-Managed EntityManager

4.2.应用程序管理的EntityManager

Conversely, the lifecycle of the EntityManager is managed by the application.

相反,EntityManager的生命周期则由应用程序管理。

In fact, we’ll manually create the EntityManager, as well as manage the lifecycle of it.

事实上,我们将手动创建EntityManager,以及管理它的生命周期。

First, let’s create the EntityManagerFactory:

首先,让我们创建EntityManagerFactory:

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

In order to create an EntityManager, we must explicitly call createEntityManager() in the EntityManagerFactory:

为了创建一个EntityManager,我们必须在EntityManagerFactory中明确调用createEntityManager()

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

Since we’re responsible for creating EntityManager instances, it’s also our responsibility to close themTherefore, we should close each EntityManager when we’re done using them.

由于我们负责创建 EntityManager 实体,所以我们也有责任关闭它们因此,当我们使用完每个EntityManager时,我们应该关闭它们。

4.3. Thread-Safety

4.3.线程安全

The EntityManagerFactory instances, and consequently, Hibernate’s SessionFactory instances, are thread-safe. So it’s completely safe in concurrent contexts to write:

EntityManagerFactory实例,以及Hibernate的SessionFactory实例,都是线程安全的。因此,在并发的情况下,它的编写是完全安全的。

EntityManagerFactory emf = // fetched from somewhere
EntityManager em = emf.createEntityManager();

On the other hand, the EntityManager instances aren’t thread-safe, and are meant to be used in thread-confined environments. This means that each thread should obtain its instance, work with it, and close it at the end.

另一方面,EntityManager实例并不是线程安全的,而是要在线程封闭的环境中使用。这意味着每个线程都应该获得它的实例,对它进行处理,并在结束时关闭它。

When using application-managed EntityManagers, it’s easy to create thread-confined instances:

在使用应用程序管理的EntityManagers时,很容易创建线程约束的实例。

EntityManagerFactory emf = // fetched from somewhere 
EntityManager em = emf.createEntityManager();
// use it in the current thread

However, things get counter-intuitive when using container-managed EntityManagers:

然而,当使用容器管理的EntityManagers时,事情就变得不那么直观了。

@Service
public class MovieService {

    @PersistenceContext // or even @Autowired
    private EntityManager entityManager;
    
    // omitted
}

It seems that one EntityManager instance should be shared for all operations. However, the container (JakartaEE or Spring) injects a special proxy instead of a simple EntityManager here. Spring, for example, injects a proxy of type SharedEntityManagerCreator

似乎一个EntityManager实例应该被共享给所有的操作。然而,容器(JakartaEE或Spring)注入了一个特殊的代理,而不是一个简单的EntityManager 这里。例如,Spring 注入了一个 SharedEntityManagerCreator 类型的代理。

Every time we use the injected EntityManager, this proxy will either reuse the existing EntityManager or create a new one. Reuse usually occurs when we enable something like Open Session/EntityManager in View

每次我们使用注入的EntityManager时,该代理将重用现有的EntityManager或创建一个新的。当我们启用诸如Open Session/EntityManager in View时,通常会发生重用。

Either way, the container ensures that each EntityManager is confined to one thread.

无论哪种方式,容器确保每个EntityManager被限制在一个线程中

5. Hibernate Entity Operations

5.Hibernate实体操作

The EntityManager API provides a collection of methods. We can interact with the database by making use of these methods.

EntityManager API提供了一个方法集合。我们可以通过使用这些方法与数据库进行交互。

5.1. Persisting Entities

5.1.持续存在的实体

In order to have an object associated with the EntityManager, we can make use of the persist() method:

为了让一个对象与EntityManager关联,我们可以利用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();
}

Once the object is saved in the database, it’s in the persistent state.

一旦对象被保存在数据库中,它就处于persistent状态。

5.2. Loading Entities

5.2.加载实体

For the purpose of retrieving an object from the database, we can use the find() method.

为了从数据库中检索一个对象,我们可以使用find()方法

Here, the method searches by the primary key. In fact, the method expects the entity class type and the primary key:

这里,该方法通过主键进行搜索。事实上,该方法希望得到实体类的类型和主键。

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

However, if we just need the reference to the entity, we can use the getReference() method instead. In effect, it returns a proxy to the entity:

然而,如果我们只需要实体的引用,我们可以使用getReference()方法代替。实际上,它返回的是实体的代理。

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

5.3. Detaching Entities

5.3.脱离实体

In the event that we need to detach an entity from the persistence context, we can use the detach() method. We pass the object to be detached as the parameter to the method:

如果我们需要从持久化上下文中分离出一个实体,我们可以使用detach()方法。我们把要分离的对象作为参数传给该方法。

em.detach(movie);

Once the entity is detached from the persistence context, it’ll be in the detached state.

一旦实体从持久化上下文中分离出来,它就会处于分离状态。

5.4. Merging Entities

5.4.合并实体

In practice, many applications require entity modification across multiple transactions. For example, we may want to retrieve an entity in one transaction for rendering to the UI. Then another transaction will bring in the changes made in the UI.

在实践中,许多应用程序需要在多个事务中修改实体。例如,我们可能想在一个事务中检索一个实体以渲染到用户界面。然后,另一个事务将带来在用户界面中的变化。

For such situations, we can make use of the merge() method. The merge method helps to bring any modifications made to the detached entity into the managed entity: 

对于这种情况,我们可以利用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. Querying for Entities

5.5.实体的查询

Furthermore, we can make use of JPQL to query for entities. We’ll invoke getResultList() to execute them.

此外,我们可以利用JPQL来查询实体。我们将调用getResultList()来执行它们。

Of course, we can use the getSingleResult() if the query returns just a single object:

当然,如果查询只返回一个对象,我们可以使用getSingleResult()

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;
}

5.6. Removing Entities

5.6.删除实体

Additionally, we can remove an entity from the database using the remove() method. It’s important to note that the object isn’t detached, but removed.

此外,我们可以使用remove()方法从数据库中删除一个实体。值得注意的是,该对象不是被分离,而是被移除。

Here, the state of the entity changes from persistent to new:

在这里,实体的状态从持久的变为新的。

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. Conclusion

6.结语

In this article, we explored the EntityManager in Hibernate. We looked at the types and configuration, and we learned about the various methods available in the API for working with the Persistence Context.

在这篇文章中,我们探讨了Hibernate中的EntityManager。我们查看了类型和配置,并了解了API中用于处理持久化上下文的各种方法。

As always, the code used in this article is available over on Github.

像往常一样,本文中使用的代码可在Github上获得