WHCSRL 技术网

关于EntityManager

一、实体的状态

① 新建状态 NEW

新创建的状态,尚未拥有持久性主键

实际上就是new了一个普通的JavaBean对象

② 持久化状态

已经拥有持久化主键并和持久化建立了上下文环境

当处在托管状态的实体Bean被管理器flush了,那么就在极短暂的时间进入了持久化状态,事务提交之后,立刻变为了游离状态。

您可以把持久化状态当做实实在在的数据库记录。

③ 游离状态

拥有持久化主键,但是没有与持久化建立上下文环境

游离状态就是提交到数据库后,事务commit后实体的状态,因为事务已经提交了,此时实体的属性任你如何改变,也不会同步到数据库,因为游离是没人管的孩子,不在持久化上下文中。

④ 删除状态 REMOVED

拥有持久化主键,已经和持久化建立了上下文环境,但是从数据库中删除

⑤ 托管状态

临时状态在调用persist()后,即可将一般的JavaBean做为了托管状态的Bean,该Bean的任何属性改动都会牵涉到数据库记录的改动。

一旦该记录flush到数据库之后,并且事务提交了,那么此对象不在持久化上下文中,即:变为了游离(没人管的孩子)状态了。

在游离状态的时候调用更新、刷新方法后,游离状态对象就变为了在持久化上下文的托管状态了。

通过管理器的find方法,将实体从数据库查询出来后,该实体也就变为了托管形态。

在这里插入图片描述

二、EntityManger && PersistenceContext

​ 被EntityManager持久化到数据库中的对象,或者从数据库拉入内存中的对象,也会同时被一个持久化上下文(PersistenceContext)管理。

这些被管理的对象统称为受管对象(Managed Object),每个受管对象都有一个唯一的id。EntityManager和PersistenceContext之间的关系,一般可以是多对一的,即多个EntityManager可以同时指向一个PersistenceContext。

这其实很好理解,就是EntityManager虽然有多个实例,但是它们背后的持久化上下文却只有一个,这样就保证了多个EntityManager所管理的受管对象拥有的ID是唯一的。

受到容器托管的EntityManager可以通过注解@PersistenceContext

@PersistenceContext
private EntityManager em;
  • 1
  • 2

三、Persistence 和EntityManagerFactory

Persistence

Persistence 类是用于获取 EntityManagerFactory 实例。该类包含一个名为 createEntityManagerFactory 的 静态方法 。
createEntityManagerFactory 方法有如下两个重载版本。
带有一个参数的方法以 JPA 配置文件 persistence.xml 中的持久化单元名为参数
带有两个参数的方法:前一个参数含义相同,后一个参数 Map类型,用于设置 JPA 的相关属性,这时将忽略其它地方设置的属性。Map 对象的属性名必须是 JPA 实现库提供商的名字空间约定的属性名。

EntityManagerFactory

EntityManagerFactory 接口主要用来创建 EntityManager 实例。该接口约定了如下4个方法:

  1. createEntityManager():用于创建实体管理器对象实例。
  2. createEntityManager(Map map):用于创建实体管理器对象实例的重载方法,Map 参数用于提供 EntityManager 的属性。
  3. isOpen():检查 EntityManagerFactory 是否处于打开状态。实体管理器工厂创建后一直处于打开状态,除非调用close()方法将其关闭。
  4. close():关闭 EntityManagerFactory 。 EntityManagerFactory 关闭后将释放所有资源,isOpen()方法测试将返回 false,其它方法将不能调用,否则将导致IllegalStateException异常。

四、EntityManager 常用的API

find

find (Class<T> entityClass,Object primaryKey);
   /* 
   第一个参数为被查询的实体类类型,第二个参数为待查找实体的主键值。
   返回指定的 OID 对应的实体类对象,
   如果这个实体存在于当前的持久化环境,则返回一个被缓存的对象;
   否则会创建一个新的 Entity, 并加载数据库中相关信息;
   若 OID 不存在于数据库中,则返回一个 null。
   */

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

getReference

getReference (Class<T> entityClass,Object primaryKey);
/*
    第一个参数为被查询的实体类类型,第二个参数为待查找实体的主键值。
    与find()方法类似
    不同的是:如果缓存中不存在指定的 Entity, EntityManager 会创建一个 Entity 类的代理,但是不会立即加载数据库中的信息,只有第一次真正使用此 Entity 的属性才加载,
    所以如果此 OID 在数据库不存在,getReference()不会返回null, 而是抛出EntityNotFoundException
*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

persist

persist (Object entity);
/*
	参数为实体对象
用于将新创建的 Entity 纳入到 EntityManager 的管理。该方法执行后,传入 persist() 方法的 Entity 对象转换成持久化状态。
	如果传入 persist() 方法的 Entity 对象已经处于持久化状态,则 persist() 方法什么都不做。
	如果对删除状态的 Entity 进行 persist() 操作,会转换为持久化状态。
	如果对游离状态的实体执行 persist() 操作,可能会在 persist() 方法抛出 EntityExistException(也有可能是在flush或事务提交后抛出)。
*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

remove

remove (Object entity);
/*
删除实例。如果实例是被管理的,即与数据库实体记录关联,则同时会删除关联的数据库记录。
*/

  • 1
  • 2
  • 3
  • 4
  • 5

merge

merge (T entity);
// merge() 用于处理 Entity 的同步。即数据库的插入和更新操作

  • 1
  • 2
  • 3

flush

 flush();

//同步持久上下文环境,即将持久上下文环境的所有未保存实体的状态信息保存到数据库中。
  • 1
  • 2
  • 3

clear

 clear()

/*
1.清除持久上下文环境,断开所有关联的实体。如果这时还有未提交的更新则会被撤消。

2.在处理大量实体的时候,如果你不把已经处理过的实体从EntityManager中分离出来,将会消耗你大量的内存。

3.调用EntityManager 的clear()方法后,所有正在被管理的实体将会从持久化内容中分离出来。

4.有一点需要说明下,在事务没有提交前(事务默认在调用堆栈的最后提交,如:方 法的返回),如果调用clear()方法,之前对实体所作的任何改变将会丢失
*/

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

persist和merge跟save的一些区别 & flush方法:

  • persist方法和save方法略微不同:如果对象有id,则persist方法不能执行insert操作,会抛出异常

  • 如果传递进persist()方法的参数不是实体Bean,会引发IllegalArgumentException

  • merge方法:

    1. 传入的对象没有id

      在这种情况下,调用merge方法,将返回一个新的对象(有id),并对这个新的对象执行insert操作。

    2. 传入的对象有id,entityManager的缓存中没有该对象,数据库中没有该记录

    在这种情况下,调用merge方法,将返回一个新的对象,并对该对象执行insert操作。

    新对象的id是数据库中这条记录的id(比如自增长的id),而不是我们自己传入的id。(其实和情况1的结果是一样的)

    1. 传入的对象有id,entityManager的缓存没有该对象,数据库中有该记录

    在这种情况下,调用merge方法,将会从数据库中查询对应的记录,生成新的对象,然后将我们传入的对象复制到新的对象,最后执行update操作。简单来说,就是更新操作。

    1. 传入的对象有id,entityManager的缓存有该对象

      在这种情况下,调用merge方法,JPA会把传入的对象赋值到entityManager的缓存中的对象,然后对entityManager缓存中的对象执行update操作。(和情况3的结果一样)

      总结

      执行merge时,如果实体ID为空,则进行insert操作, 如果有ID则进行update操作。

  • flush方法

    1. ORM框架执行的一些更新数据库的方法,其实质是在更新缓存,只有调用了flush()后才会将缓存同步到数据库,即真正执行SQL语句,但是这时并没有真正将数据保存进数据库,需要事务commit后才能全部保存。
    2. 一般flush后立刻就会进行事务的提交。
    3. 当EntityManager对象在一个sessionbean 中使用时,它是和服务器的事务上下文绑定的。
    4. 4.在一个session bean 中,服务器的事务默认地会在调用堆栈的最后提交(如:方法的返回)。
  • 总而言之,若传入save的实体对象没有主键则进行insert操作,否则进行update操作

五、利用EntityManager批量插入

有的时候我们需要循环保存数据,当保存大量数据的时候,

如果到最后才提交所有数据,那么数据库的负载可能会比较大。

我们可以这样做,每500个记录就提交(flush)一次。

public class BatchDao {

    @PersistenceContext
    private EntityManager entityManager;

    //每500个记录提交一次
    private static final  int BATCH_SIZE = 500;

    /**
     * 批量插入
     *
     * @param list 需要插入的list
     * @param <T>  实体类型
     */
    public <T> void batchInsert(List<T> list) {
        if (!ObjectUtils.isEmpty(list)) {
            for (int i = 0; i < list.size(); i++) {
                 //变成托管状态
                entityManager.persist(list.get(i));
                if (i %% BATCH_SIZE == 0) {
                    entityManager.flush();
                    entityManager.clear();
                }
            }
            //变成持久化状态
            entityManager.flush();
            //变成游离状态
            entityManager.clear();
        }
    }

    /**
     * 批量更新
     *
     * @param list 要更新的list
     * @param <T>  实体类型
     */
    public <T> void batchUpdate(List<T> list) {
        if (!ObjectUtils.isEmpty(list)) {

            for (int i = 0; i < list.size(); i++) {
                //变成托管状态
                entityManager.merge(list.get(i));
                if (i %% BATCH_SIZE == 0) {
                    entityManager.flush();
                    entityManager.clear();
                }
            }
            //变成持久化状态
            entityManager.flush();
            //变成游离状态
            entityManager.clear();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
推荐阅读