EJB JPA

JPA EntityManager详解

持久化上下文(Persistence Contexts)的相关知识,内容包括如何从Java EE容器中创建EntityManager对象、如何从Java SE中创建EntityManager对象、持久化上下文与事务(Transction)的关系,以及实体管理器工厂(Entity Manager Factory)的相关内容。
通过本章的学习,读者将深入掌握JPA中有关持久化上下文、事务处理的相关知识,从而能够更加深入地应用JPA。


11.1 获得EntityManager对象

那么如何获得EntityManager对象呢?这又是JPA中另外一个很重要的问题。


11.1.1  Java EE环境与J2SE环境

在详细讲述EntityManager对象之前,读者首先要分清楚两个概念,即Java EE环境与J2SE环境。因为在本章后面的学习中要经常提到这两个概念,所以读者一定要先理解它们,为以后的学习打好基础。


— Java EE环境,包括EJB容器和Web容器。

(1)Web容器:只运行Web应用的容器,例如Tomcat就是开源的Web容器,它可以运行JSP、Servlet等。

(2)EJB容器:运行在EJB组件的容器,提供EJB组件的状态管理、事务管理、线程管理、远程数据资源访问、连接管理和安全性管理等系统级服务。例如JBoss为EJB容器和Web容器(Web容器是集成了Tomcat)结合。

部署在EJB容器中的JAR包都可以认为是运行在EJB容器中。但JBoss中的Web应用,比如war包中的类就不是运行在EJB容器中,而是运行在Web容器中。

— J2SE环境

最普通Java运行环境,例如一个HelloWorld的Java程序就是运行在J2SE的环境中,通常使用main入口方法作为程序启动的触发。

如图11-1所示,它说明了Java EE与J2SE环境的关系。

11.1.2  两种类型的EntityManager对象

根据EntityManager对象的管理方式,可以有以下两种类型。

— 容器托管的(container-managed)EntityManager对象

容器托管的EntityManager对象最简单,程序员不需要考虑EntityManager连接的释放,以及事务等复杂的问题,所有这些都交 给容器去管理。容器托管的EntityManager对象必须在EJB容器中运行,而不能在Web容器和J2SE的环境中运行。本书前面讲述的 EntityManager对象都是通过注入 @PersistenceContext注释来获得的,其实,这种获得EntityManager对象的方式就是容器托管的。

— 应用托管的(application-managed)EntityManager对象

应用托管的EntityManager对象,程序员需要手动地控制它的释放和连接、手动地控制事务等。但这种获得应用托管的 EntityManager对象的方式,不仅可以在EJB容器中应用,也可以使 JPA脱离EJB容器,而与任何的Java环境集成,比如说Web容器、J2SE环境等。所以从某种角度上来说,这种方式是JPA能够独立于EJB环境运 行的基础。

理想状态下,最好是选用容器托管的EntityManager对象的方式,但在特殊的环境下,还是需要使用应用托管的EntityManager对象这种方式。

正是因为应用托管的EntityManager对象的连接释放、事务控制比较复杂,所以在使用时涉及的相关内容比较多,这些内容将在本章后面部分详细讲述,这里读者应对两种方式有一个大致的了解,两种EntityManager对象类型的比较如表11-1所示。

表11-1  容器托管与应用托管的EntityManager对象对比

比较内容    容器托管的(container-managed)EntityManager对象      应用托管的(application-managed)EntityManager对象

获得方式    两种方式:1 @PersistenceContex注入 2 JNDI获得            EntityManagerFactory创建

支持事务    JTA                                                                                    JTA、RESOURCE_LOCAL

运行环境     EJB容器                                                                            EJB容器、Web容器、J2SE环境

11.1.3  容器托管的(container-managed)EntityManager对象

容器托管的EntityManager对象只能运行在EJB容器中。所以可以这样理解,只有在EJB-JAR包中,才可以获得容器托管的EntityManager对象,否则只能获得应用托管的EntityManager对象。

在EJB容器中获得EntityManager对象主要有两种方式,即@PersistenceContext注释注入和JNDI方式获得。

11.1.3.1  通过@PersistenceContext注释注入

这种方式获得EntityManager对象最为常用,例如下面代码所示。

  1. @Stateless  
  2.   
  3. public class CustomerService implements ICustomerService {  
  4.   
  5.     @PersistenceContext(unitName = "jpaUnit")  
  6.   
  7.     private EntityManager entityManager;  
  8.   
  9.     public List<CustomerEO> findAllCustomers() {  
  10.   
  11.         Query query = entityManager.createQuery("SELECT c FROM CustomerEO c");  
  12.   
  13.         List<CustomerEO> result = query.getResultList();  
  14.   
  15.         for (CustomerEO c : result) {  
  16.   
  17.             System.out.println(c.getId()+","+c.getName());  
  18.   
  19.         }  
  20.   
  21.         return result;  
  22.   
  23.     }  
  24.   


在使用此种方式创建EntityManager对象时,需要注意以下几个问题。

— @PersistenceContext注释中,其中unitName为persistence.xml文件中<persistence-unit>元素中的属性“name”的值,表示要初始化哪个持久化单元,如下所示。

Xml代码
  1. <persistence>  
  2.   
  3.     <persistence-unit name="jpaUnit" transaction-type="JTA">  
  4.   
  5.     </persistence-unit>  
  6.   
  7. </persistence> 
— @PersistenceContext注释中还可以配置其他的设置,它的定义如下所示。

Java代码
  1. @Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME)  
  2.   
  3. public @interface PersistenceContext{  
  4.   
  5.     String name() default "";  
  6.   
  7.     String unitName() default "";  
  8.   
  9.     PersistenceContextType type default TRANSACTION;  
  10.   
  11.     PersistenceProperty[] properties() default {};  
  12.   


— 其中PersistenceContextType可以设置创建EntityManager对象时,持久化上下文的作用范围,它的意义在于对有状态的Bean(Stateless Bean)可以跨事务操作实体。它主要有两种方式,定义如下所示。
Java代码
  1. public enum PersistenceContextType {  
  2.   
  3.     TRANSACTION,  
  4.   
  5.     EXTENDED  
  6.   


默认情况下使用TRANSACTION,有关TRANSACTION方式和EXTENDED方式创建EntityManager对象的异同,将在下文中详细讲述,这里读者简单了解一下即可。

11.1.3.2  通过JNDI的方式获得

如果指定了@PersistenceContext注释中的name值,则设置了持久化上下文的JNDI名称。通过SessionContext可以创建EntityManager对象。

例如,下面代码为通过JNDI方式获得EntityManager对象。

  1. @Stateless  
  2.   
  3. @PersistenceContext(name="jpa")  
  4.   
  5. public class CustomerService implements ICustomerService {  
  6.   
  7.     @Resource  
  8.   
  9.     SessionContext ctx;  
  10.   
  11.     public List<CustomerEO> findAllCustomers() {  
  12.   
  13.         EntityManager entityManager = (EntityManager) ctx.lookup("jpa");  
  14.   
  15.         Query query = entityManager.createQuery("SELECT c FROM CustomerEO c");  
  16.   
  17.         List<CustomerEO> result = query.getResultList();  
  18.   
  19.         for (CustomerEO c : result) {  
  20.   
  21.             System.out.println(c.getId()+","+c.getName());  
  22.   
  23.         }  
  24.   
  25.         return result;  
  26.   
  27.     }  
  28.   
  29. }  

11.1.4  应用托管的(application-managed)EntityManager对象

应用托管的EntityManager对象,不仅可以在Java EE环境中获得,也可以应用在J2SE的环境中。但无论是在什么情况下获得的EntityManager对象,都是通过实体管理器工厂 (EntityManagerFactory)对象创建的。所以如何获得应用托管的EntityManager对象关键是 EntityManagerFactory对象如何获得。

下面就分别讲述在EJB容器、Web容器和J2SE环境中如何获得EntityManagerFactory对象。

11.1.4.1  EJB容器中获得

在EJB容器中,EntityManagerFactory对象可以通过使用注入@PersistenceUnit注释获得,例如下面代码为在EJB容器中,获得应用托管的EntityManager对象的方法。

  1. @Stateless  
  2.   
  3. public class CustomerService implements ICustomerService {  
  4.   
  5.     @PersistenceUnit(unitName="jpaUnit")  
  6.   
  7.     private EntityManagerFactory emf;  
  8.   
  9.     public List<CustomerEO> findAllCustomers() {  
  10.   
  11.         /**创建EntityManager对象*/  
  12.   
  13.         EntityManager em = emf.createEntityManager();  
  14.   
  15.         Query query = em.createQuery("SELECT c FROM CustomerEO c");  
  16.   
  17.         List<CustomerEO> result = query.getResultList();  
  18.   
  19.         for (CustomerEO c : result) {  
  20.   
  21.             System.out.println(c.getId()+","+c.getName());  
  22.   
  23.         }  
  24.   
  25.         /**关闭EntityManager */  
  26.   
  27.         em.close();  
  28.   
  29.         return result;  
  30.   
  31.     }  
  32.   
  33. }  

通过以上的EntityManager对象代码,可以总结出以下几个问题。

— 应用托管的EntityManager对象,要在代码中手动地创建和关闭,例如下面代码所示。

EntityManager em = emf.createEntityManager();

/**其他的业务逻辑*/

em.close();

这点正是与容器托管的EntityManager对象的最大不同之处。事实上,容器托管的EntityManager对象,它的创建和关闭是由容器负责管理的,所以不需要编写代码来控制。

— 应用托管的EntityManager对象,都是通EntityManagerFactory对象来创建的。在容器中可以通过使用注入@PersistenceUnit注释的方法实现,它的定义如下所示。

  1. @Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME)  
  2.   
  3. public @interface PersistenceUnit{  
  4.   
  5.     String name() default "";  
  6.   
  7.     String unitName() default "";  
  8.   


其中,属性unitName为persistence.xml文件中<persistence-unit>元素中的属性“name”的值,表示要初始化哪个持久化单元,与@PersistenceContext注释中unitName属性相同。

11.1.4.2  Web容器中获得

在Web容器中,EntityManagerFactory对象也可以通过使用注入@PersistenceUnit注释获得。例如,下面代码为在 Servlet中,获得应用托管的EntityManager对象的方法。
  1. public class TestServlet extends HttpServlet {  
  2.   
  3.     @PersistenceUnit(unitName = "jpaUnit")  
  4.   
  5.     private EntityManagerFactory emf;  
  6.   
  7.     public TestServlet() {  
  8.   
  9.         super();  
  10.   
  11.     }  
  12.   
  13.     public void doGet(HttpServletRequest request, HttpServletResponse response)  
  14.   
  15.             throws ServletException, IOException {  
  16.   
  17.         doPost(request, response);  
  18.   
  19.     }  
  20.   
  21.     public void doPost(HttpServletRequest request, HttpServletResponse response)  
  22.   
  23.             throws ServletException, IOException {  
  24.   
  25.         response.setContentType("text/html");  
  26.   
  27.         PrintWriter out = response.getWriter();  
  28.   
  29.         out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional //EN\">");  
  30.   
  31.         out.println("<HTML>");  
  32.   
  33.         out.println("  <HEAD><TITLE>A Servlet</TITLE></HEAD>");  
  34.   
  35.         out.println("  <BODY>");  
  36.   
  37.         if (emf != null) {  
  38.   
  39.             /**创建EntityManager 对象*/  
  40.   
  41.             EntityManager entityManager = emf.createEntityManager();  
  42.   
  43.             try {  
  44.   
  45.                 Query query = entityManager  
  46.   
  47.                         .createQuery("SELECT c FROM CustomerEO c");  
  48.   
  49.                 List<CustomerEO> result = query.getResultList();  
  50.   
  51.                 for (CustomerEO c : result) {  
  52.   
  53.                     System.out.println(c.getId() + "," + c.getName());  
  54.   
  55.                 }  
  56.   
  57.             } finally {  
  58.   
  59.                 /**关闭EntityManager*/  
  60.   
  61.                 entityManager.close();  
  62.   
  63.             }  
  64.   
  65.         }  
  66.   
  67.         out.println("  </BODY>");  
  68.   
  69.         out.println("</HTML>");  
  70.   
  71.         out.flush();  
  72.   
  73.         out.close();  
  74.   
  75.     }  
  76.   
  77. }  

由于是通过注入方法获得的EntityManagerFactory对象,所以EntityManagerFactory对象的创建不需要手动创建和关闭,这里与在EJB容器中的获得方法相同。@Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME) public @interface PersistenceUnit{ String name() default ""; String unitName() default ""; }




事务管理是JPA中另一项重要的内容,了解了JPA中的事务管理,能够进一步掌握JPA的使用。

事务管理是对一系列操作的管理,它最终只有两个结果,要么成功,要么失败。一旦失败,所有的操作将回滚到初始状态。一旦成功,才最终提交,最终持久化。事务管理对银行系统最为典型。例如一个人去银行取款,他取款的钱此时大于银行账户中的钱,此时交易失败,所以取款不成功,事务回滚到操作前的状态。

在JPA中,对于实体的“CRUD”基本操作,其中涉及事务的是“C”、“U”和“D”,即“新建”、“更新”和“删除”,因为这些操作都会影响数据库中的数据变化,所以必须使用事务保证其一致性;对于“R”查询,只是查询数据,没有对数据产生变化,所以并不需要控制事务。

所以,一说到事务,读者首先应确定所使用的操作是否需要关联事务,先要界定事务所有效使用的范围。

11.4.1  事务与EntityManager

EntityManager对象的事务管理方式有两种,分别为JTA和RESOURCE_LOCAL,即Java Transaction API方法和本地的事务管理。

JPA中的事务类型通过persistence.xml文件中的“transaction-type”元素配置。例如,配置事务为JTA方式的代码如下所示。

<persistence>

    <persistence-unit name="demo" transaction-type="JTA">

        //其他配置省略

    </persistence-unit>

</persistence>

如果使用RESOURCE_LOCAL管理事务,则配置代码如下所示。

<persistence>

    <persistence-unit name="demo" transaction-type="RESOURCE_LOCAL">

        //其他配置省略

    </persistence-unit>

</persistence>

除了在配置文件时指明了事务的类型,不同的事务类型,不同类型的EntityManager对象,在代码中控制事务也是不同的。表11-2为不同的运行环境、不同的EntityManager对象所支持的事务类型。

表11-2  事务类型与EntityManager

运行环境

类型

J2EE环境

J2SE环境

EJB容器

Web容器

应用托管的EntityManager

JTARESOURCE_LOCAL

JTARESOURCE_LOCAL

RESOURCE_LOCAL

容器托管的EntityManager

JTA

不支持

不支持

从表11-2中可以看出,对于不同的EntityManager类型与所运行的环境,所支持的事务类型是不一样的。

其中两种情况下最为简单,一种是容器托管的EntityManager只能运行在EJB容器中,只能采用JTA的方式管理事务;另一种是J2SE环境下,只能使用应用托管的EntityManager并且只能采用RESOURCE_LOCAL的方式管理事务。本节的事务只针对这两种情况讲述,而对于应用托管的EntityManager在EJB容器和Web容器中由于都可以选择不同的事务管理方式,情况比较复杂,所以将在第11.5节中详细讲述。

11.4.2  JTA管理事务

JTA事务(Java Transaction API)是J2EE规范中有关事务的标准。它是容器级别的事务,只能运行在J2EE服务器中。它的最大优势是可以支持分布式的事务,如果系统采用的是分布式的数据库,那么只能选择JTA管理EntityManager事务。

使用JTA管理EntityManager事务时,需要注意以下几个问题。

— JTA事务只能运行在J2EE的环境中,即EJB容器中和Web容器中;而在J2SE环境中只能使用RESOURCE_LOCAL管理事务。

— 容器托管的EntityManager对象只能采用JTA的事务,而不能采用RESOURCE_LOCAL事务。

在第11.3节中,已经简单了解了一些JTA事务与EntityManager之间的关系,但当Bean的方法中又调用了另一个Bean的方法时,那么此时事务传播(Propagation)是如何进行的?下面就深入了解事务的传播与持久化上下文的关系。

有这样一个记录日志的会话Bean,它负责记录相关的日志信息等,它有一个记录日志的方法recordLog,代码如下所示。

@Stateless

public class LogService implements ILogService {

    @PersistenceContext(unitName = "jpaUnit")

    private EntityManager entityManager;

    /**记录日志*/

    public void recordLog(Integer id, String reason) {

        LogEO log = new LogEO();

        log.setId(id);

        log.setReason(reason);

        entityManager.persist(log);

    }

}

此时在CustomerService的会话Bean中,addCustomer方法中需要新建客户后,再调用日志组件来记录日志信息,代码如下所示。

@Stateless

public class CustomerService implements ICustomerService {

    @PersistenceContext(unitName = "jpaUnit")

    private EntityManager entityManager;

    @EJB

    private ILogService logService ;

    public CustomerEO addCustomer(CustomerEO customer) {

        entityManager.persist(customer);

        logService.recordLog(customer.getId(), "新建Customer");

        return customer;

    }

}

此时EntityManager对象是容器托管的,并且设置的事务类型为JTA。下面仔细分析一下,当在一个EJB组件中调用另外一个EJB组件时,事务的传播与持久化上下文环境的关系。

— 当客户端调用addCustomer方法时,此时容器自动关联一个JTA的事务,一个事务开始,这里将该事务记为事务A。

— 当调用persist方法持久化客户时,EntityManager对象发现当前有一个JTA的事务A,则此时将EntityManager对象的事务附加到JTA的事务A中,并且创建了一个新的持久化上下文。

— 调用日志组件的recordLog方法,容器发现调用了另外一个EJB的方法,所以首先检查当前是否存在事务,由于当前状态下存在事务A,所以将recordLog方法的事务附加到事务A中(由于默认情况下,CustomerService的事务类型是REQUIRED)。

— 当进入recordLog方法时,再次调用persist方法持久化日志时,由于此时EntityManager对象的事务是附加到JTA事务A中的,所以仍与之前调用的persist方法时所在的持久化上下文相同,所以,可以直接调用持久化客户后的customer.getId(),来获得持久化客户的Id值。虽然在一个EJB组件中调用了另外一个EJB组件的方法,但两次调用的persist方法所在的持久化上下文是相同的。

— recordLog方法结束,又回到addCustomer方法中,此时事务A提交,一个持久化上下文也就随之结束了。

11.4.3  RESOURCE_LOCAL管理事务

RESOURCE_LOCAL事务数据库本地的事务。它是数据库级别的事务,只能针对一种数据库,不支持分布式的事务。对于中小型的应用,可以采用RESOURCE_LOCAL管理EntityManager事务。

使用RESOURCE_LOCAL管理EntityManager事务时需要注意以下几个问题。

— 在J2SE环境中,只能使用RESOURCE_LOCAL管理EntityManager事务,并且EntityManager对象是以应用托管方式获得的。

— 代码中使用RESOURCE_LOCAL管理事务时,要通过调用EntityManager的getTransaction()方法获得本地事务对象。

例如,在J2SE环境中,使用RESOURCE_LOCAL管理EntityManager事务的代码如下所示。

public class CustomerClient {

    public static void main(String[] args) {

        EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpaUnit");

        EntityManager entityManager = emf.createEntityManager();

        try {

            /** 事务开始 */

            entityManager.getTransaction().begin();

            CustomerEO customer = new CustomerEO();

            customer.setName("Janet");

            customer.setEmail("janetvsfei@yahoo.com.cn");

            customer.setAsset(100000.00);

            /** 事务提交 */

            entityManager.getTransaction().commit();

        } finally {

            entityManager.close();

            emf.close();

        }

    }

}

★ 提示 ★

采用RESOURCE_LOCAL管理事务时,要保证数据库支持事务。例如使用MySQL时,需要设置数据库的引擎类型为“InnoDB”,而“MyISAM”类型是不支持事务的。

— 在代码中,entityManager.getTransaction()方法获得本地事务EntityTransaction对象,然后通过该对象提供的方法来控制本地的事务。有关EntityTransaction的API将在下一节讲述。

— 控制本地事务时,开始一个新事务,使用begin()方法;事务完成后,使用commit()方法提交。控制事务时,并没有调用rollback()方法回滚,这是因为在事务开始后,一旦有异常抛出,EntityTransaction对象将自动回滚,所以并不需要显式地调用rollback()方法回滚。

11.4.4  EntityTransaction API

下面来看本地事务EntityTransaction中所定义的方法EntityTransaction API,以及它们的作用,如下所示。

EntityTransaction API

package javax.persistence;

public interface EntityTransaction {

    public void begin();

    public void commit();

    public void rollback();

    public void setRollbackOnly();

    public boolean getRollbackOnly();

    public boolean isActive();

}

下面具体来看各个方法所表示的意义,每个方法都从作用、方法参数、异常信息,以及返回值这几个方面来讲述。

— public void begin()

作用:声明事务开始。

方法参数:无。

异常信息:如果此时事务处于激活状态,即isActive()为true,将抛出IllegalStateException异常。

返回值:无返回值。

— public void commit()

作用:提交事务,事务所涉及的数据的更新将全部同步到数据库中。

方法参数:无。

异常信息:如果此时事务处于未激活状态,即isActive()为false,将抛出IllegalState Exception异常;如果此时提交不成功,则抛出RollbackException异常。

返回值:无返回值。

— public void rollback()

作用:事务回滚。

方法参数:无。

异常信息:如果此时事务处于未激活状态,即isActive()为false,将抛出IllegalState Exception异常;如果此时回滚失败,则抛出PersistenceException异常。

返回值:无返回值。

— public void setRollbackOnly()

作用:设置当前的事务只能是回滚状态。

方法参数:无。

异常信息:如果此时事务处于未激活状态,即isActive()为false,将抛出IllegalState Exception异常。

返回值:无返回值。

— public boolean getRollbackOnly()

作用:获得当前事务的回滚状态。

方法参数:无。

异常信息:如果此时事务处于未激活状态,即isActive()为false,将抛出IllegalState Exception异常。

返回值:true表示只能回滚状态。

— public boolean isActive ()

作用:判断当前事务是否处于激活状态。

方法参数:无。

异常信息:如果发生了未知的异常,将抛出PersistenceException异常。

返回值:true表示当前事务处于激活状态,false表示当前事务未处于激活状态。

11.5  应用托管的EntityManager的持久化上下文

通过表11-2所总结的各种情况,应用托管EntityManager对象在EJB容器中和Web容器中,可选择的事务类型比较复杂,既可以支持JTA,又可以支持RESOURCE_LOCAL。下面讲述在这两种情况下,如何控制事务。

11.5.1  无状态的会话Bean与JTA事务(事务范围)

在会话Bean里以注入的方式获得EntityManagerFactory对象,不需要负责它的关闭,所以此时,只需要控制EntityManager的打开和关闭。当客户端每次调用Bean中的方法时,都首先创建EntityManager对象,然后在方法结束前关闭EntityManager对象。EntityManager对象的事务使用的是容器自动管理的事务JTA。

代码如下所示。

@Stateless

public class CustomerService implements ICustomerService {

   

    @PersistenceUnit(unitName="jpaUnit")

    private EntityManagerFactory emf;

       

    public CustomerEO findCustomerById(Integer customerId) {

        EntityManager em = emf.createEntityManager();

        CustomerEO customer = em.find(CustomerEO.class, customerId);

        em.close();

        return customer;

    }

    public void placeOrder(Integer customerId, OrderEO order) {

        EntityManager em = emf.createEntityManager();

        CustomerEO customer = em.find(CustomerEO.class, customerId);

        customer.getOrders().add(order);

        em.merge(customer);

        em.close();

    }

}

11.5.2  无状态的会话Bean与JTA事务(扩展范围)

与上个会话Bean中的管理方式不同,此时EntityManager对象为Bean的属性,当Bean初始化后,也就是标注@PostConstruct方法后,创建EntityManager对象;当Bean销毁前,也就是标注@PreDestroy方法后,关闭EntityManager对象,所以EntityManager对象是整个的Bean的声明周期中。当客户端调用需要关联事务的方法时,需要使用joinTransaction()方法合并到上一次的事务中。

代码如下所示。

@Stateless

public class CustomerService implements ICustomerService {

   

    @PersistenceUnit(unitName="jpaUnit")

    private EntityManagerFactory emf;

       

    private EntityManager em;

   

    @PostConstruct

    public void init (){

        em = emf.createEntityManager();

    }

   

    public CustomerEO findCustomerById(Integer customerId) {

        /**查询不需要关联事务*/

        CustomerEO customer = em.find(CustomerEO.class, customerId);

        em.clear();

        return customer;

    }

    public void placeOrder(Integer customerId, OrderEO order) {

        /**

        *EntityManager 对象的作用范围是这个Bean的生命周期

        *所以,每次使用时要合并到上一次的事务中

        */

        em.joinTransaction();

        CustomerEO customer = em.find(CustomerEO.class, customerId);

        customer.getOrders().add(order);

        em.merge(customer);

        /**

        * 手动脱离当前事务和持久化上下文

        */

        em.flush();

        em.clear();

    }

    @PreDestroy

    public void destroy(){

        em.close();

    }

}

11.5.3  有状态的会话Bean与JTA事务

同样是EntityManager对象在整个的Bean的声明周期中,但由于会话Bean此时是有状态的Bean,所以当客户端调用任何方法时,都处在同一个持久化上下文中。所以每次并不需要调用clear()方法来手动地脱离当前的上下文,但每次客户端的调用仍需要使用joinTransaction()方法合并到上一次的事务中。

代码如下所示。

@Stateful

public class CustomerService implements ICustomerService {

   

    @PersistenceUnit(unitName="jpaUnit")

    private EntityManagerFactory emf;

       

    private EntityManager em;

    private  CustomerEO customer ;

   

    @PostConstruct

    public void init (){

        em = emf.createEntityManager();

    }

   

    public CustomerEO findCustomerById(Integer customerId) {

        customer = em.find(CustomerEO.class, customerId);

        return customer;

    }

    public void placeOrder(Integer customerId, OrderEO order) {

        em.joinTransaction();

        customer.getOrders().add(order);

    }

    @Remove

    public void destroy(){

        em.close();

    }

}

 

 

11.5.4  RESOURCE_LOCAL事务

前面三节的例子讲述的是JTA事务,当在J2SE环境中,必须采用RESOURCE_LOCAL事务,而且需要手动创建和关闭EntityManagerFactory、EntityManager对象。关联事务时要使用EntityManager对象的getTransaction().begin()和getTransaction().commit()方法。

代码如下所示。

public class CustomerService {

    private EntityManagerFactory emf;

    private EntityManager em;

   

    public CustomerService (){

        emf = Persistence.createEntityManagerFactory("jpaUnit");

        em = emf.createEntityManager();

    }

    private  CustomerEO customer ;

       

    public CustomerEO findCustomerById(Integer customerId) {

        customer = em.find(CustomerEO.class, customerId);

        return customer;

    }

    public void placeOrder(Integer customerId, OrderEO order) {

        em.getTransaction().begin();

        customer.getOrders().add(order);

        em.getTransaction().commit();

    }

    public void destroy(){

        em.close();

        emf.close();

    }

}

 

===============================

包含DAO层实例


通过容器来连接数据库的,程序通过JNDI来调用

在建立项目之前先进入jboss 目录:\jboss-4.2.3.GA\docs\examples\jca 找到mysql-ds.xml ,因为我使用的是mysql所以就要它另存为其它地方,如果是oracle或db2找相对应的就OK

然后修改下数据库连接及其它设置修改如下

  1. <?xml version="1.0"   
  2.   
  3. encoding="UTF-8"?>  
  4.   
  5.   
  6. <datasources>  
  7.   <local-tx-datasource>  
  8.     <jndi-name>demoDS</jndi-name>  
  9.     <connection-  
  10.   
  11. url>jdbc:mysql://127.0.0.1:3306/demo</connection-url>  
  12.     <driver-class>com.mysql.jdbc.Driver</driver-class>  
  13.     <user-name>root</user-name>  
  14.     <password>root</password>  
  15.     <min-pool-size>3</min-pool-size>  
  16.     <max-pool-size>10</max-pool-size>  
  17.     <exception-sorter-class-  
  18.   
  19. name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter<  
  20.   
  21. /exception-sorter-class-name>  
  22.     <metadata>  
  23.        <type-mapping>mySQL</type-mapping>  
  24.     </metadata>  
  25.   </local-tx-datasource>  
  26. </datasources> 


  1. <?xml version="1.0"   encoding="UTF-8"?>  
  2. <persistence xmlns="http://java.sun.com/xml/ns/persistence"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.     xsi:schemaLocation="http://java.sun.com/xml/ns/persistence  
  5.     http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"   
  6.   
  7. version="1.0">  
  8.       
  9.     <persistence-unit name="demo" transaction-type="JTA">  
  10.         <jta-data-source>java:demoDS</jta-data-source>  
  11.     </persistence-unit>  
  12.     
  13. </persistence> 

  1. public interface JpaDao {  
  2.     /** 
  3.      * 查询接口 
  4.      * @param jpaql jpaql语句 
  5.      * @param parameters 数组参数 
  6.      * @return 返回对象数组 
  7.      */  
  8.     public List<Object> find(String jpaql, Object[] parameters);  
  9.     /** 
  10.      * 查询接口 
  11.      * @param jpaql jpaql语句 
  12.      * @param parameters Map作为参数key指占位符字符串,value指具体的值对象 
  13.      * @return 返回对象数组 
  14.      */  
  15.     public List<Object> find(String jpaql, Map<String, Object> parameters);  
  16.       
  17.     /** 
  18.      * 保存持久化对象 
  19.      * @param obj 持久化对象 
  20.      */  
  21.     public void save(Object obj);  
  22.       
  23.       

  1. @Stateless  
  2. @Local //申明一个本地的会话bean 
  3. public class JpaDaoImpl implements JpaDao {
  4. @PersistenceContext(unitName="demo") EntityManager em;  
  5.      
  6.     @SuppressWarnings("unchecked")  
  7.     public List<Object> find(String jpaql, Object[] parameters) {  
  8.         //查询接口  
  9.         Query query = em.createQuery(jpaql);  
  10.           
  11.         for (int i = 0; i < parameters.length; i++) {  
  12.               
  13.             Object parameter = parameters[i];  
  14.               
  15.             query.setParameter(i+1, parameter);  
  16.         }  
  17.           
  18.         return query.getResultList();  
  19.     }  
  20.   
  21.     @SuppressWarnings("unchecked")  
  22.     public List<Object> find(String jpaql, Map<String, Object> parameters) {  
  23.         Query query = em.createQuery(jpaql);  
  24.   
  25.         Iterator<Entry<String, Object>> it = parameters.entrySet().iterator();  
  26.   
  27.         while (it.hasNext()) {  
  28.   
  29.             Map.Entry<String, Object> entry = it.next();  
  30.   
  31.             query.setParameter(entry.getKey(), entry.getValue());  
  32.         }  
  33.   
  34.         return query.getResultList();  
  35.     }  
  36.   
  37.     public void save(Object obj) {  
  38.         //类似hibernate.save(Object ojb);  
  39.         em.persist(obj);  
  40.           
  41.     }  

  1. public interface UserService {  
  2.       
  3.     public User verifyUser(String username,String password);  
  4.       
  5.     public void saveUser(User user);  
  6.       
  7.     public void saveUser(List<User> users);  
  8.       

  1. /*这是一个可远程访问的Session Bean */  
  2. @Stateless // @Stateless 声明这是一个无状态sessionBean  
  3. @Remote    // @Remote 声明这是一个可远程调用的sessionBean,如果未声明默认为local(本地),本地可以节省网络开销,需在同一个jvm中,内存调用  
  4.   
  5. //声明事物 事物分为两种(CMP、BMP)cmp可以理解为api事物(也可理解为用程序来控制事物),bmp可以理解为容器来处理事物(无需要写代码)  
  6. //@TransactionManagement(TransactionManagementType.CONTAINER) 我们用的是bmp事物,我完全是用白话文说的哈,可能说的不对,见谅  
  7. //我书读的不多,也许说得不够准确,但大概意思是说到了的,如果不明白建议google、baidu  
  8. @TransactionManagement(TransactionManagementType.CONTAINER)  
  9.  
  10. public class UserServiceImpl implements UserService {  
  11.     //@EJB 这里指调用同一个jvm的ejb对象  
  12.     @EJB
  13. JpaDao jpaDao;  
  14.       
  15.     /*验证用户信息*/  
  16.     public User verifyUser(String username, String password) {  
  17.         List<Object> users =jpaDao.find("from User u where u.userName=?1 and u.passWord=?2"new Object[]{username,password});  
  18.         return (User) (users.isEmpty()?null:users.get(0));  
  19.     }  
  20.       
  21.     //保持用户信息  
  22.     //常用的事物类型2种  
  23.     // 1、REQUIRED 大概意思是,如果事物不存在创建一个,如果事物存在就调用当前事物,还是那句话不明白就google  
  24.     // 2、REQUIRES_NEW 大概意思,无论事物是否存在都,再次创建一个事物  
  25.     @TransactionAttribute(TransactionAttributeType.REQUIRED)//这里声明一个事物类型  
  26.     public void saveUser(User user) {  
  27.         jpaDao.save(user);  
  28.     }  
  29.       
  30.     //  做一个类似批量保存,本应该抽象在dao层来做批量,但为了事物所以写在这个方法中~ 加入事物后保持一致性,如果有一条不成功,回滚  
  31.     //  这个就有点像spring的aop面向切面编程,意思就是这个方法开始之前执行一个行为,结束之前在执行一个行为,事物也就可以用在这里了  
  32.     //  调用public void saveUser(List<User> users)这个方法之前打开一个事物,当这个方法执行完之前提交事物,并关闭当前会话session  
  33.     //  自我感觉说的很明白了哈,如果还是无法理解请google..呵呵  
  34.     @TransactionAttribute(TransactionAttributeType.REQUIRED)  
  35.     public void saveUser(List<User> users) {  
  36.           
  37.         for(User user : users){  
  38.             jpaDao.save(user);  
  39.         }  
  40.           
  41.     }  
  42.   


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值