JPA -- EntityManager常用API详解

            实体状态和转换

 JPA -- EntityManager常用API详解

 

实体状态详解

Ø  临时状态:

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

Ø  托管状态

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

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

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

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

Ø  持久化状态

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

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

Ø  游离状态

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

Ø  销毁对象

  一般要删除一个持久化对象的时候都是先find出来,之后调用remove方法删之,此时这个对象就是销毁对象,实际上就是瞬时对象的另一种形态罢了。

EntityManager一些常用的API

SELECT

Ø  find() 和getReference()

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

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

 异同:

1.当在数据库中没有找到记录时,find()方法会返回null,

而getReference() 方法会抛出javax.persistence.EntityNotFoundException异常,

2.调用getReference()方法,返回的其实并不是实例对象,而是一个代理。当你要使用实体时,才会真正的调用查询语句来查询实例对象

3.另外getReference()方法不保证 entity Bean已被初始化。

4.如果传递进getReference()或find()方法的参数不是实体Bean,都会引发 IllegalArgumentException

INSERT

Ø  persist()

  将临时状态的实体持久化到数据库

Person person = new Person();

person.setName(name);

//把数据保存进数据库中

em.persist(person);

persist方法:使对象由临时状态变为持久化状态,就是执行INSERT操作。

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

2.和hibernate的save()方法有些不同:如果对象有id,则不能执行insert操作,会抛出异常

Ø  Merge()

  详见下方


UPDATE

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

publicvoid updatePerson() {

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

person.setName("lihuoming");//方法执行完后即可更新数据

}

Ø  Merge

1.      传入的对象没有id

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

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

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

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

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

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

简单来说,就是更新操作。

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

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

(和情况3的结果一样)

总结:执行merge时,如果实体ID为空,则进行insert操作

     如果有ID则进行update操作。

DELETE

Ø  Remove()

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

1.      如果级联关系cascade=CascadeType.ALL,在删除person 时候,也会把级联对象删除。把cascade属性设为cascade=CascadeType.REMOVE 有同样的效果。

em.remove (person);

2.如果传递进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,就可以删除

 

 

 

 

JPQL -- createQuery()

SELECT

Ø  getResultList()

除了使用find()或getReference()方法来获得Entity Bean之外,你还可以通过JPQL得到实体Bean。

要执行JPQL语句,你必须通过EntityManager的createQuery()或createNamedQuery()方法创建一个Query 对象

Query query = em.createQuery("select pfrom Person p");

List<Map<String,Object>> result= query.getResultList();

createNamedQuery是命名查询,需要@NamedQuery配合在实体中创建明明查询。

 

Ø  getSingleResult()

返回查询的第一条数据,可以进行强转,如:

查询数目:

Query query = em.createQuery("select count(1)from Person p");

Long num = (Long)query. getSingleResult ();

强转为实体:

Query query = em.createQuery("select pfrom Person p");

User user = (User)query. getSingleResult ();

 

Ø  executeUpdate()

执行更新删除操作,返回受影响的记录数。

Query query = em.createQuery("deletefrom Person");

int result =query.executeUpdate(); //影响的记录数

JPQL没有插入语句即不能执行insert语句。

 

Ø  关于JPQL参数问题

1.      使用标识符

Query query =em.createQuery("delete from Person p where p.id := id");

query.setParameter("name","张三");

int result =query.executeUpdate(); //影响的记录数

 

2.      使用索引下标

Query query =em.createQuery("delete from Person p where p.id = ?1 ");

query.setParameter(1, "张三");

int result =query.executeUpdate(); //影响的记录数


SQL – createNaiveQuery()

 

       用法基本同createQuery(),只不过这里使用的不是JPQL而是SQL

Ø  将查询到的数据映射成实体:

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

… ..

        }

           }

 

------------------------------------ SQLEND ----------------------------------------------------------

 

EntityManage一些不常用的方法

 

Ø  refresh()

User user = em.find(User.class, 1);

//第二次同样的查询不会访问数据库

user = em.find(User.class, 1);

运行以上代码,发现调用了两次find,但是只执行了一次select语句,这是缓存导致的。

执行refresh()方法刷新缓存,容器会把数据库中的新值重写进实体。

User user = em.find(User.class, 1);

em.refresh(user);

Ø  contains()

判断实体是否还在EntityManage的管理下,或者说是否属于当前持久上下文环境。

contains()方法使用一个实体作为参数,如果这个实体对象当前正被持久化内容管理,返回值为true,否则为false。

如果传递的参数不是实体 Bean,将会引发一个IllegalArgumentException.

User user = em.find(User.class, 1);

if (em.contains(user)){

//正在被持久化内容管理

}else{

//已经不受持久化内容管理

}


Ø  clear()

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

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

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

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

所以建议你在调用clear()方法之前先调用flush()方法保存更改。

 

Ø  flush()

1. ORM框架执行的一些更新数据库的方法,其实质是在更新缓存,只有调用了flush()后才会将缓存同步到数据库,即真正执行SQL语句,但是这时并没有真正将数据保存进数据库,需要事务commit后才能全部保存。

2. 一般flush后立刻就会进行事务的提交。

3. 当EntityManager对象在一个sessionbean 中使用时,它是和服务器的事务上下文绑定的。

4.在一个session bean 中,服务器的事务默认地会在调用堆栈的最后提交(如:方法的返回)。

 

Ø  setFlushMode()

  改变实体管理器的Flush模式

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

em.setFlushMode(FlushModeType.COMMIT);

1.      FlushModeType.AUTO:

默认情况下除了在事务提交时flush,在进行查询时(除了find()和getreference()查询)也会进行一次flush,比如使用JPQL查询前会进行一次flush。

使用场合:在 大量更新数据的过程中没有任何查询语句(除了find()和getreference()查询)的执行。

2.      FlushModeType.COMMIT:

刷新只有在事务提交时才发生,查询不触发。

使用场合:在大量更新数据的过程中存在查询语句(除了find()和 getreference()查询)的执行。

  其实上面两种模式最终反映的结果是:JDBC 驱动跟数据库交互的次数。

JDBC 性能最大的增进是减少JDBC 驱动与数据库之间的网络通讯。

FlushModeType.COMMIT模式使更新只在一次的网络交互中完成,而FlushModeType.AUTO 模式可能需要多次交互(触发了多少次Flush 就产生了多少次网络交互

 

 

 

Ø  isOpen ()

 判断当前的实体管理器是否是打开状态

Ø  getTransaction ()

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

 

Ø  大量数据分批提交

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

如果到最后才提交所有数据,那么数据库的负载可能会比较大。我们可以这样做,每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(); //变成游离状态

}

}

}

------------------------------------------------  END ------------------------------------------------------------------

以上是我摘录网上自己整理的,希望读者能够动起手来过一遍加深印象哦。