介绍 Hibernate EntityManager

池庆
2023-12-01

EntityManager属于Java Persistence API的一部分. 它主要实现了JPA 2.0规范定义的编程接口和生命周期规则,而且通过EntityManager api 可以访问Persistence Context。本文我们介绍EntityManager的配置、类型以及其API.

EntityManager配置

引入依赖

首先我们引入hibernate依赖:

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

当然也要增加相应数据库依赖,这里引入mysql:

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

配置EntityManager

为了演示,我们定义Movie实体和对应的数据库表MOVIE。我们将使用EntityManager API 操作数据库对象。

实体定义:

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

    private String movieName;
    // standard constructor, getters, setters
}

下面介绍xml文件方式配置EntityManager。当EntityManagerFactory 被创建了,持久化实现搜索类路径下的*META-INF/persistence.xml* file文件。

<persistence-unit name="com.crm.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>

上面定义了EntityManager管理数据存储的持久化单元。并且定义了方言、jdbc连接属性,Hibernate不关心数据库,仅依赖通过这些属性配置连接底层数据库。当然也可以通过Java Config方式进行配置。

两种类型EntityManager

有两者EntityManager,容器管理和应用管理。下面详细讲解。

容器管理EntityManager

容器通过EntityManagerFactory 创建EntityManager:

@PersistenceContext
EntityManager entityManager;

这意味着容器负责事务开始、提交或回滚。类似的,容器也负责关闭EntityManager ,所以可以安全使用,无需手工清理。甚至当我们关闭容器管理的EntityManager ,会抛出IllegalStateException

应用管理EntityManager

相反应用管理的EntityManager生命周期不同。首先我们需要手动创建,而且也需要管理其生命周期。

首先,我们创建EntityManagerFactory

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

为了创建EntityManager,需要显示调用createEntityManager方法:

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

既然我们负责创建EntityManager实例,也要负责关闭。因此,使用完需要关闭每个EntityManager实例。

线程安全

*EntityManagerFactory* 和 **Hibernate 的 SessionFactory实例都是线程安全的,所以可以在并发场景下是安全的:

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

需要注意的是,*EntityManager* 实例不是线程安全的,因此在线程限制环境种使用,需要每个线程获取、使用、最后要关闭。

当使用应用管理EntityManagers时,很容易创建线程限制的实例:

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

然而,当使用容器管理EntityManagers时,事情变得与直觉相反。例如:

@Service
public class MovieService {

    @PersistenceContext 
    private EntityManager entityManager;
    
    // omitted
}

看上去似乎EntityManager被所有操作共享,但实际上容器(Spring 或 J2EE)注入代理,而不是简单EntityManager实例。比如Spring 注入 *SharedEntityManagerCreator。每次使用注入的EntityManager实例,代理会重用或创建新的实例。

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

Hibernate实例操作

EntityManager API 提供了一组方法可以和数据库进行交互。

持久化实体

使用persist方法关联对象和EntityManager:

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

当数据保存在数据库中,它是持久化状态。

加载实体

从数据库种获取实体,可以使用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));

游离实体

一些场景需要实体脱离持久化上下文,可以使用detach方法,传入对象作为方法的参数:

em.detach(movie);

一旦实体从持久化上下文种脱离,其状态为游离状态。

合并实体

实际中,很多应用需要实体通过多个事务进行修改。举例,在界面中通过一个事务获取对象,然后两一个事务通过界面进行修改。我们可以利用merge方法实现。

首先利用detach方法使得实体处于游离状态,然后进行修改,最后在事务中进行合并。

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

查询实体

我们可以使用JPQL查询实体,getResultList返回多个实体,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. R

删除实体

可以使用remove方法从数据库删除实体。需要提醒的是,该对不是detach状态,而是被删除了。

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

上面对象是从持久化状态转为新建状态。

总结

本文介绍了Hibernate 的EntityManager,包括其类型、配置以及如何使用API操作持久化上下文。

 类似资料: