Session bean or MD bean 对Entity bean的操作(包括所有的query, insert, update, delete操作)都是通过 EntityManager 实例来完成的。 EntityManager 是由 EJB 容器自动地管理和配置的,不需要用户自己创建。
那么 Session bean or MD bean 如何获得 EntityManager 实例呢??
非常简单,就是通过下列代码进行依赖注入:
Public class sessionbean1{
@PersistenceContext
EntityManager em;
。。。
}
注意:如果 persistence.xml 文件中配置了多个 <persistence-unit> 。那么在注入 EntityManager 对象时 必须指定持久化名称 ,通过 @PersistenceContext 注释的 unitName 属性 进行指定,例:
@PersistenceContext( unitName="foshanshop" )
EntityManager em;
如果只有一个 <persistence-unit> ,不需要明确指定。
请注意:Entity Bean 被 EntityManager 管理时, EntityManager 会 跟踪他的状态改变,在任何决定更新实体 Bean 的时候便会把发生改变的值同步到数据库中(跟hibernate一样)。但是如果entity Bean 从 EntityManager 分离后,他是不受管理的, EntityManager 无法跟踪他的任何状态改变 。
EntityManager 一些 常用的 API (包含 query, insert, update, delete 操作)
1) get entity —— find() or getReference()
Person person = em.find(Person.class,1);
/*
try {
Person person = em.getReference (Person.class, 1);
} catch (EntityNotFoundException notFound) {
// 找不到记录 ...
}
*/
当在数据库中 没有找到记录时, getReference() 和 find() 是有区别的 , find() 方法会返回 null ,而 getReference() 方法会抛出 javax.persistence.EntityNotFoundException 例外 , 另外 getReference() 方法不保证entity Bean 已被初始化 。如果传递进 getReference() 或 find() 方法的参数不是实体 Bean ,都会引发 IllegalArgumentException 例外
2) insert —— persist()
Person person = new Person();
person.setName(name);
// 把数据保存进数据库中
em.persist(person) ;
如果传递进 persist() 方法的参数不是实体 Bean ,会引发 IllegalArgumentException
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);
执行 em.merge(person) 方法时,容器的工作规则 :
1> 如果此时容器中已经存在一个受容器管理的具有相同 ID 的 person 实例,容器将会把参数 person 的内容拷贝进这个受管理的实例, merge() 方法返回受管理的实例,但参数 person 仍然是分离的不受管理的。容器在决定 Flush 时把实例同步到数据库中。
2> 容器中不存在具有相同 ID 的 person 实例。容器根据传进的 person 参数 Copy 出一个受容器管理的 person 实例,同时 merge() 方法会返回出这个受管理的实例,但参数 person 仍然是分离的不受管理的。容器在决定 Flush 时把实例同步到数据库中。
如果传递进 merge () 方法的参数不是实体 Bean ,会引发一个 IllegalArgumentException 。
4) Delete —— Remove()
Person person = em.find(Person.class, 2);
// 如果级联关系 cascade=CascadeType.ALL ,在删除 person 时候,也会把级联对象删除。
// 把 cascade 属性设为 cascade=CascadeType.REMOVE 有同样的效果。
em.remove (person);
如果传递进 remove () 方法的参数不是实体 Bean ,会引发一个 IllegalArgumentException
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()
如果你怀疑当前被管理的实体已经不是数据库中最新的数据,你可以通过 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()
的 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 进行编码 ,不过这种方法并不可取,强烈建议不要使用。在 Weblogic 中,你也可以通过此方法获取对 Kodo 的访问。