当前位置: 代码迷 >> J2EE >> The Java EE 六 Tutorial 第32章 JPA简介 (三) 管理实体
  详细解决方案

The Java EE 六 Tutorial 第32章 JPA简介 (三) 管理实体

热度:692   发布时间:2016-04-22 00:26:32.0
The Java EE 6 Tutorial 第32章 JPA简介 (三) 管理实体
管理实体

实体由实体管理器(entity manager)管理,即javax.persistence.EntityManager实例。每个EntityManager实例都和一个持久化上下文关联:存在于特定数据存储的一套受管的实体实例。一个持久化上下文定义实体的作用域,实体在此作用域下创建、持久、删除。EntityManager接口定义了用来和持久化上下文交互的方法。



EntityManager接口

EntityManager API可创建、删除持久化实体实例,通过实体主键查找实体,也可以在实体上执行查询。


容器管理的实体管理器

对于容器托管的实体管理器(container-managed entity manager),管理器实例的持久化上下文会由容器自动传播(propagate),范围是使用了带JTA事务的实体管理器的应用组件。
With a an EntityManager instance’s persistence context is automatically propagated by the container to all application components that use the EntityManager instance within a single Java Transaction API (JTA) transaction.

JTA事务通常跨应用组件调用。要完成JTA事务,这些组件通常需要访问同一个持久化上下文。通过将实体管理器(EntityManager)注入到应用组件来实现,只要使用javax.persistence.PersistenceContext注解即可。持久化上下文会同JTA事务一起被自动传播,指向同一个持久化单元的实体管理器引用都可以在事务内访问持久化上下文。通过自动传播持久化上下文,应用组件不需要为了保证单一事务而来回传递EntityManager实例的引用。Java EE容器会管理容器托管实体管理器的生命周期。

要获得EntityManager实例,只需要将实体管理器注入应用组件:

@PersistenceContext
EntityManager  em;



应用管理的实体管理器
对于应用管理的实体管理器(application-managed entity manager),不同的是,持久化上下文吗不会传播到应用组件。EntityManager实例的生命周期也是由应用控制。

应用管理的实体管理器适用于:应用程序需要访问的持久化上下文不与横跨多个EntityManager实体的JTA事务绑定。 (Application-managed entity managers are used when applications need to access a persistence context that is not propagated with the JTA transaction across EntityManager instances in a particular persistence unit.)这种情况下,每个EntityManager创建一个新的独立的持久化上下文。EntityManager及其关联的持久化上下文由应用程序显式的创建。因EntityManager非线程安全而不能直接注入EntityManager实例时,也使用应用管理的实体管理器。EntityManagerFactory实例是线程安全的。

这种情况下,应用使用javax.persistence.EntityManagerFactory的createEntityManager方法,创建EntityManager实例。

要获取EntityManager实例,你必须先取得EntityManagerFactory实例。可以使用javax.persistence.PersistenceUnit注解,将EntityManagerFactory实例注入应用组件:

@PersistenceUnit
EntityManagerFactory  emf;

从EntityManagerFactory实例取得EntityManager实例:

EntityManager  em  = emf.createEntityManager();

应用管理的实体管理器不会自动传播JTA事务上下文,这种应用需要手动获取JTA事务管理器,并在执行实体操作时,添加事务分界(demarcation)信息。javax.transaction.UserTransaction接口定义了以下方法:开始事务(begin)、提交事务(commit)、回滚事务(roll back)。可以使用@Resource注解将UserTransaction注入到带有一个实例变量中:

@Resource
UserTransaction utx;

可以调用UserTransaction.begin方法来开始一个事务。当所有实体操作完成后,调用UserTransaction.commit方法来提交一个事务。UserTransaction.rollback方法可用来回滚当前事务。

下面的例子演示了如何在应用中使用应用管理的实体管理器来管理事务:

@PersistenceContext EntityManagerFactory  emf; EntityManager  em;


@Resource
UserTransaction utx;
...
em  = emf.createEntityManager();
try {
utx.begin();
em.persist(SomeEntity);
em.merge(AnotherEntity);
em.remove(ThirdEntity);
utx.commit();
} catch   (Exception e)  {
utx.rollback();
}

用EntityManager查找实体
EntityManager.find方法可根据实体的主键,在数据存储中查找实体:

@PersistenceContext
EntityManager  em;
public void  enterOrder(int custID, Order  newOrder) {
Customer cust = em.find(Customer.class, custID);
cust.getOrders().add(newOrder);
newOrder.setCustomer(cust);
}

管理实体实例的生命周期
你能使用EntityManager实例调用实体上的操作,来管理实体实例。实体实例可以处于四种状态之一:new(新建), managed(受控), detached(分离), removed(删除)。

■ 新建实体实例没有持久化标识,而且也没有和任何持久化上下文关联。
■ 受控实体实例有持久化标识,并且和持久化上下文关联。
■ 分离实体实例有持久化标识,但是当前没有和持久化上下文关联。
■ 删除的实体有持久化标识,也和持久化上下文关联,但是计划将从数据存储中删除。


持久化实体实例
新建实体实例可以通过调用persist方法来变为受控状态并持久,另外由其他实体persist操作引起的级联调用也可以,只要其他实体在关系注解中将cascade元素设置为PERSIST或者ALL。这意味着当persist操作关联的事务完成后,实体的数据会保存到数据库。如果实体已经是受控状态,则persist操作会被忽略,但是persist操作会级联到关联的实体,即



关系注解中,cascade元素被设为PERSIST或ALL的实体。如果persist操作调用在删除实体实例上,那实体就会变为受控状态。如果实体是分离状态,则可能persist抛出IllegalArgumentException异常,也可能事务提交失败。

@PersistenceContext
EntityManager  em;
...
public LineItem  createLineItem(Order order,  Product  product,
int quantity) {
LineItem  li = new LineItem(order, product, quantity);
order.getLineItems().add(li);
em.persist(li);
return li;
}

persist操作的传播范围,包括同调用persist的实体相关的所有实体,即关系注解中cascade元素被设置为ALL或PERSIST的所有实体:

@OneToMany(cascade=ALL,  mappedBy="order")
public  Collection<LineItem> getLineItems()  {
return lineItems;
}

删除实体实例
受控实体实例在调用remove方法时被删除,另外由其他实体remove操作引起的级联调用也可以,只要其他实体在关系注解中将cascade元素设置为PERSIST或者ALL。如果在新建实体实例上remove操作调用,则remove操作会被忽略,但是remove操作会级联到关联的实体,即关系注解中,cascade元素被设为REMOVE或ALL的实体。如果在分离实体上调用remove,则可能抛出IllegalArgumentException,也可能事务提交失败。如果在删除实体上调用remove方法,则会被忽略。当事务完成时,或者调用flush操作时,数据的数据会从数据存储中删除。

public void  removeOrder(Integer orderId)  {
try {
Order  order   = em.find(Order.class, orderId);
em.remove(order);
}...

本例中,关联到此订单的LineItem(条目)实体也会被删除,因为关系注解中
Order.getLineItems的级联设置为cascade=ALL。


将实体数据同步到数据库
当实体关联的事务提交时,持久化实体的状态就被同步到数据库。如果受控实体处于和另一受控实体的双向关系中,则会由关系的所有者来决定数据的持久化。



要强制同步受控实体到数据存储,可以调用EntityManager实例的flush方法。如果实体关联另一个实体,且关系注解的cascade元素也被设置为PERSIST或ALL,则flush方法调用时,关联实体的数据也会同步到数据存储。

如果实体被删除,调用flush方法会从数据存储中删除数据。

持久化单元

持久化单元定义了一个实体类的集合,这些实体类受控于应用中的EntityManager实例。这个实体类的集合代表了位于某个数据存储内的数据。

实体单元定义在persistence.xml配置文件。下面是一个persistence.xml文件的例子:

<persistence>
<persistence-unit name="OrderManagement">
<description>This unit  manages orders and customers.
It does  not  rely on any vendor-specific features and can
therefore be deployed  to  any persistence provider.
</description>
<jta-data-source>jdbc/MyOrderDB</jta-data-source>
<jar-file>MyOrderApp.jar</jar-file>
<class>com.widgets.Order</class>
<class>com.widgets.Customer</class>
</persistence-unit>
</persistence>

这个文件定义了一个持久化单元,叫做OrderManagement,其使用了JTA-aware(支持JTA)的数据源:jdbc/MyOrderDB。jar-file和class元素指定了受控的持久类:实体类、嵌入类、及带映射超类。尽管class元素显式的列举了受控持久类,jar-file元素也指定了打包的持久化单元可见的JAR文件,其中包括了持久类。

jta-data-source(用于指定支持JTA的数据源)和non-jta-data-source(用于指定不支持JTA的数据源)元素指定了容器使用的数据源的全局JNDI名称。

对于JAR文件和目录,如果其子目录META-INF下有persistence.xml文件,就叫做持久化单元的根。持久化单元的作用域由持久化单元的根决定。每个持久化单元必须由一个名称唯一标示,该名称必须在该持久化单元的作用域内唯一。



持久化单元可以被打包为WAR或EJB JAR文件的一部分,也可被打包为JAR文件,以供放入WAR或EAR文件使用。

■ 如果你将持久化单元打包成EJB JAR文件中的一套类,那persistence.xml文件应该放在EJB JAR文件的META-INF目录
■ 如果你将持久化单元打包成WAR文件中的一套类,那persistence.xml文件应该放在WAR文件的WEB-INF/classes/META-INF目录
■ 如果你将持久化单元打包成JAR文件,并将其放入WAR或者EAR文件,那JAR文件应该放在:
■        WAR包的WEB-INF/lib目录
■        EAR文件的library目录


注意 – 在JPA1.0中,JAR文件可以被放在EAR文件的根目录,来作为持久化单元的根。这种做法现在已经不再支持了。可移植程序应该使用EAR文件的library目录,来作为持久化单元的根
  相关解决方案