Skip to main content
 首页 » 编程设计

介绍 Hibernate EntityManager

2022年07月19日164lori

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操作持久化上下文。


本文参考链接:https://blog.csdn.net/neweastsun/article/details/118051998