EntityManager常用API

简介: EntityManager常用API


JPA实体状态分析


图片.png

实体状态详解

       最新的Hibernate文档中为Hibernate对象定义了四种状态(原来是三种状态,面试的时候基本上问的也是三种状态),分别是:瞬时态(new, or transient)、持久态(managed, or persistent)、游状态(detached)和移除态(removed,以前Hibernate文档中定义的三种状态中没有移除态),如下图所示,就以前的Hibernate文档中移除态被视为是瞬时态。


  • 瞬时状态(Transient
实际上就是new了一个普通的JavaBean对象。
特点:持久化对象没有唯一标识OID.没有纳入Session的管理.
  • 瞬时态:当new一个实体对象后,这个对象处于瞬时态,即这个对象只是一个保存临时数据的内存区域,如果没有变量引用这个对象,则会被JVM的垃圾回收机制回收。这个对象所保存的数据与数据库没有任何关系,除非通过Session的save()、saveOrUpdate()、persist()、merge()方法把瞬时态对象与数据库关联,并把数据插入或者更新到数据库,这个对象才转换为持久态对象。



  • 游离状态(Detached):
特点:持久化对象有唯一标识OID,没有纳入到Session管理.
  • 游离态:当Session进行了close()、clear()、evict()或flush()后,实体对象从持久态变成游离态,对象虽然拥有持久和与数据库对应记录一致的标识值,但是因为对象已经从会话中清除掉,对象不在持久化管理之内,所以处于游离态(也叫脱管态)。游离态的对象与临时状态对象是十分相似的,只是它还含有持久化标识。


  • 持久化状态(Persistent):
特点:持久化对象有唯一标识OID.已经纳入到Session的管理.
***** 结论:持久化持久态对象有自动更新数据库的能力.

  • 持久态:持久态对象的实例在数据库中有对应的记录,并拥有一个持久化标识(ID)。对持久态对象进行delete操作后,数据库中对应的记录将被删除,那么持久态对象与数据库记录不再存在对应关系,持久态对象变成移除态(可以视为瞬时态)。持久态对象被修改变更后,不会马上同步到数据库,直到数据库事务提交。


EntityManager一些常用的API(包含query, insert, update, delete操作)


1)get entity —— find() or getReference()

Person person = em.find(Person.class,1);


  • 当在数据库中没有找到记录时,getReference()和find()是有区别的,
  • find()方法会返回null,
  • 而getReference() 方法会抛出javax.persistence.EntityNotFoundException例外,
  • 调用getReference()方法,返回的其实并不是实例对象,而是一个代理。当你要使用实体时,才会真正的调用查询语句来查询实例对象
  • 另外getReference()方法不保证 entity Bean已被初始化。
  • 如果传递进getReference()或find()方法的参数不是实体Bean,都会引发 IllegalArgumentException


2)insert —— persist()

Person person = new Person();
person.setName(name);
//把数据保存进数据库中
em.persist(person);


  • persist方法的:使对象由临时状态变为持久化状态
  • 如果传递进persist()方法的参数不是实体Bean,会引发IllegalArgumentException
  • 和hibernate的save()方法有些不同:如果对象有id,则不能执行insert操作,会抛出异常


3)update —— 分2种情况

  情况1:当实体正在被容器管理时,你可以调用实体的set方法对数据进行修改,在容器决定flush时(这个由Container自行判断),更新的数据 才会同步到数据库,而不是在调用了set方法对数据进行修改后马上同步到数据库。如果你希望修改后的数据马上同步到数据库,你可以调用EntityManager.flush()方法

public void updatePerson() {
try {
Person person = em.find(Person.class, 1);
person.setName("lihuoming"); //方法执行完后即可更新数据
} catch (Exception e) {
e.printStackTrace();
}
}



     情况2:在实体Bean已经脱离了EntityManager的管理时,你调用实体的set方法对数据进行修改是无法同步更改到数据库的。你必须调用 EntityManager.merge()方法。调用之后,在容器决定flush时(这个由container自行判断),更新的数据才会同步到数据 库。如果你希望修改后的数据马上同步到数据库,你可以调用EntityManager.flush()方法

public boolean updatePerson(Person person) {
try {
em.merge(person);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}



   下面的代码会调用上面的方法。因为下面的第二行代码把实体Bean 返回到了客户端,这时的实体Bean已经脱离了容器的管理,在客户端对实体Bean进行修改,最后把他返回给EJB 容器进行更新操作:

PersonDAO persondao = (PersonDAO) ctx.lookup("PersonDAOBean/remote");
Person person = persondao.getPersonByID(1); //此时的person 已经脱离容器的管理
person.setName("张小艳");
persondao.updatePerson(person);



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


  • merge 情况2:传入的对象有id,entityManager的缓存中没有该对象,数据库中没有该记录:
在这种情况下,调用merge方法,将返回一个新的对象,并对该对象执行insert操作。
新对象的id是数据库中这条记录的id(比如自增长的id),而不是我们自己传入的id。
(其实和情况1的结果是一样的)



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



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



4)Delete —— Remove()

Person person = em.find(Person.class, 2);
//如果级联关系cascade=CascadeType.ALL,在删除person 时候,也会把级联对象删除。
//把cascade属性设为cascade=CascadeType.REMOVE 有同样的效果。
em.remove (person);



  • 如果传递进remove ()方法的参数不是实体Bean,会引发一个IllegalArgumentException
  • remove()方法不能移除游离对象,只能移除持久化对象
Order order = new Order();
order.setId(140);
entityManager.remove(order);
  • 上面这段代码会抛出异常,因为order是我们自己创建的对象,也就是游离对象必须这样写:
Order order = new Order();
order = entityManager.find(Order.class, 140);
entityManager.remove(order);


  • 这段代码中的order是从数据库中获取的,也就是持久化对象
  • hibernate的delete()方法,只要对象有Id,就可以删除



5)HPQL query —— createQuery()


  • 除了使用find()或getReference()方法来获得Entity Bean之外,你还可以通过JPQL得到实体Bean。
  • 要执行JPQL语句,你必须通过EntityManager的createQuery()或createNamedQuery()方法创建一个Query 对象
  • // 执行查询语句
Query query = em.createQuery("select p from Person p where p. name=’黎明’");
List result = query.getResultList();
Iterator iterator = result.iterator();
while( iterator.hasNext() ){
//处理Person
}



  • // 执行更新语句
Query query = em.createQuery("update Person as p set p.name =?1 where p. personid=?2");
query.setParameter(1, “黎明”);
query.setParameter(2, new Integer(1) );
int result = query.executeUpdate(); //影响的记录数


  • // 执行删除语句
Query query = em.createQuery("delete from Person");
int result = query.executeUpdate(); //影响的记录数



6)SQL query —— createNaiveQuery()

  • 注意:该方法是针对SQL语句,而不是HPQL语句
//我们可以让EJB3 Persistence 运行环境将列值直接填充入一个Entity 的实例,
//并将实例作为结果返回.
Query query = em.createNativeQuery("select * from person", Person.class);
List result = query.getResultList();
if (result!=null){
Iterator iterator = result.iterator();
while( iterator.hasNext() ){
Person person= (Person)iterator.next();
… ..
}
}



  • // 直接通过SQL 执行更新语句
Query query = em.createNativeQuery("update person set age=age+2");
query.executeUpdate();



7)Refresh entity —— refresh()

Order order= entityManager.find(Order.class, 170);
order= entityManager.find(Order.class, 170);


  • 运行以上代码,发现调用了两次find,但是只执行了一次select语句,这是缓存导致的。
1. Order order= entityManager.find(Order.class, 170);
2. entityManager.refresh(order);



  • 只调用了一次find方法,却执行了两次select语句,这是因为refresh方法会去查看缓存中的数据状态和数据库中是否一致,因此又执行了一次select语句


  • 如果你怀疑当前被管理的实体已经不是数据库中最新的数据,你可以通过refresh()方法刷新实体,容器会把数据库中的新值重写进实体。
  • 这种情况一般发 生在你获取了实体之后,有人更新了数据库中的记录,这时你需要得到最新的数据。
  • 当然你再次调用find()或getReference()方法也可以得到 最新数据,但这种做法并不优雅。
Person person = em.find(Person.class, 2);
//如果此时person 对应的记录在数据库中已经发生了改变,
//可以通过refresh()方法得到最新数据。
em.refresh (person);



8)Check entity是否在EntityManager管理当中 —— contains()

  • 判断一个实例是否属于当前持久上下文环境管理的实体
  • contains()方法使用一个实体作为参数,如果这个实体对象当前正被持久化内容管理,返回值为true,否则为false。
  • 如果传递的参数不是实体 Bean,将会引发一个IllegalArgumentException.
Person person = em.find(Person.class, 2);
。。。
if (em.contains(person)){
//正在被持久化内容管理
}else{
//已经不受持久化内容管理
}



9)分离所有当前正在被管理的实体 —— clear()


  • 清除持久上下文环境,断开所有关联的实体。如果这时还有未提交的更新则会被撤消。
  • 在处理大量实体的时候,如果你不把已经处理过的实体从EntityManager中分离出来,将会消耗你大量的内存。
  • 调用EntityManager 的clear()方法后,所有正在被管理的实体将会从持久化内容中分离出来。
  • 有一点需要说明下,在事务没有提交前(事务默认在调用堆栈的最后提交,如:方 法的返回),如果调用clear()方法,之前对实体所作的任何改变将会掉失,
  • 所以建议你在调用clear()方法之前先调用flush()方法保存更 改



10)将实体的改变立刻刷新到数据库中 —— flush()

  • 当EntityManager对象在一个session bean 中使用时,它是和服务器的事务上下文绑定的。
  • EntityManager在事务提交时并且同步它的内容。
  • 在一个session bean 中,服务器的事务默认地会在调用堆栈的最后提交(如:方法的返回)。
例子1:在方法返回时才提交事务
public void updatePerson(Person person) {
try {
Person person = em.find(Person.class, 2);
person.setName("lihuoming");
em.merge(person);
//后面还有众多修改操作
} catch (Exception e) {
e.printStackTrace();
}
//更新将会在这个方法的末尾被提交和刷新到数据库中
}




  • 为了只在当事务提交时才将改变更新到数据库中,容器将所有数据库操作集中到一个批处理中,这样就减少了代价昂贵的与数据库的交互。
  • 当你调用 persist( ), merge( )或remove( )这些方法时,更新并不会立刻同步到数据库中,直到容器决定刷新到数据库中时才会执行,
  • 默认情况下,容器决定刷新是在“相关查询”执行前或事务提交时发 生,
  • 当然“相关查询”除find()和getreference()之外,这两个方法是不会引起容器触发刷新动作的,默认的刷新模式是可以改变的。

如果你需要在事务提交之前将更新刷新到数据库中,你可以直接地调用EntityManager.flush()方法。

这种情况下,你可以手工地来刷新数据 库以获得对数据库操作的最大控制。

public void updatePerson(Person person) {
try {
Person person = em.find(Person.class, 2);
person.setName("lihuoming");
em.merge(person);
em.flush();//手动将更新立刻刷新进数据库
//后面还有众多修改操作
} catch (Exception e) {
e.printStackTrace();
}
}



11)改变实体管理器的Flush模式 —— setFlushMode()


  • setFlushMode()的Flush模式有2种类型:AUTO and COMMIT。AUTO为缺省模式。你可以改变他的值,如下:
entityManager.setFlushMode(FlushModeType.COMMIT);



  • FlushModeType.AUTO:
刷新在查询语句执行前(除了find()和getreference()查询)或事务提交时才发生,
使用场合:在 大量更新数据的过程中没有任何查询语句(除了find()和getreference()查询)的执行。



  • FlushModeType.COMMIT:
刷新只有在事务提交时才发生,
使用场合:在大量更新数据的过程中存在查询语句(除了find()和 getreference()查询)的执行。



  • 其实上面两种模式最终反映的结果是:JDBC 驱动跟数据库交互的次数。
JDBC 性能最大的增进是减少JDBC 驱动与数据库之间的网络通讯。
FlushModeType.COMMIT模式使更新只在一次的网络交互中完成,
而FlushModeType.AUTO 模式可能需要多次交互(触发了多少次Flush 就产生了多少次网络交互)



12)获取持久化实现者的引用 —— getDelegate()


  • 用过getDelegate()方法,你可以获取EntityManager持久化实现者的引用,
  • 如Jboss EJB3的持久化产品采用Hibernate,可以通过getDelegate()方法获取对他的访问,如:
HibernateEntityManager manager = (HibernateEntityManager)em.getDelegate();



  • 获得对Hibernate的引用后,可以直接面对Hibernate进行编码,不过这种方法并不可取,强烈建议不要使用。


13)判断当前的实体管理器是否是打开状态--isOpen ()

14)返回资源层的事务对象。EntityTransaction实例可以用于开始和提交多个事务--getTransaction ()

15)大量数据分批提交

1. 有的时候我们需要循环保存数据,当保存大量数据的时候,
2. 如果到最后才提交所有数据,那么数据库的负载可能会比较大。我们可以这样做,每30个记录就提交(flush)一次。



代码如下(每到30条记录的时候就强制提交):

public void updateBatch(List<Z> list) {
  for (int i = 0; i < list.size(); i++) {
   entityManager.merge(list.get(i)); //变成托管状态
   if (i % 30 == 0) {
    entityManager.flush(); //变成持久化状态
    entityManager.clear(); //变成游离状态
   }
  }
 }
 public void saveBatch(List<Z> list) {
  for (int i = 0; i < list.size(); i++) {
   entityManager.persist(list.get(i)); //变成托管状态
   if (i % 30 == 0) {
    entityManager.flush(); //变成持久化状态
    entityManager.clear(); //变成游离状态
   }
  }
 }



参考来源: http://blog.csdn.net/fobdddf/article/details/19479073

参考来源: http://blog.csdn.net/dufufd/article/details/54408727

参考来源:http://toknowme.iteye.com/blog/2235981



目录
相关文章
|
API
05JavaWeb基础 - Servlet的相关API
05JavaWeb基础 - Servlet的相关API
37 0
|
3月前
|
API
Collection常用API
Collection常用API
41 1
|
3月前
|
Java 数据库连接 API
Hibernate 中的标准 API 是什么?
【8月更文挑战第21天】
33 0
|
6月前
|
JSON Java 应用服务中间件
|
Java API Spring
SpringAOP 关键API详解
在Spring AOP中,JoinPoint和ProceedingJoinPoint是两个非常重要的概念,它们都是AOP中的切点。 因此,JoinPoint和ProceedingJoinPoint的区别在于,ProceedingJoinPoint包含了一个proceed()方法,该方法用于执行目标方法,而JoinPoint则不包含该方法。在编写AOP切面时,如果需要在目标方法执行前或执行后做一些处理,可以使用JoinPoint;如果需要在目标方法执行过程中做一些处理,可以使用ProceedingJoinPoint。
648 0
|
JSON 前端开发 JavaScript
Servlet API详解
本文我们主要介绍Servlet里的关键API,也就是HttpServlet、HttpServletRequest、HttpServletResponse这三个类中一些方法的使用。
160 0
Servlet API详解
|
API 网络安全
RestFul api 批量删除
写 Restflu Api 接口遇到批量删除问题,百度后有两种解决方法,基本上不建议使用 DELETE
838 0
|
SQL Java 数据库连接
Hibernate的Api使用
Hibernate的Api使用
|
存储 API 调度
Quartz的重要接口API(二)
Quartz的重要接口API(二)
694 0
Quartz的重要接口API(二)