`
zzc1684
  • 浏览: 1191734 次
  • 性别: Icon_minigender_1
  • 来自: 广州
文章分类
社区版块
存档分类
最新评论

JPA事务总结

    博客分类:
  • JPA
阅读更多

事务管理是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对象是容器托管的,并且设置的事务类型为JPA。下面仔细分析一下,当在一个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的getTransac- tion()方法获得本地事务对象。

例如,在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();

    }

}

分享到:
评论

相关推荐

    分布式事务实践 解决数据一致性

    4-3 Jpa事务实例 4-4 Jms事务原理 4-5 Jms-session事务实例 4-6 Jms-spring事务实例 4-7 外部事务与JTA 4-8 JTA单数据源事务实例 4-9 JTA多数据源事务实例 第5章 分布式系统 介绍了分布式系统的定义、实现原则和几...

    springboot-multipledatasources:springboot中多数据源整合的总结

    springboot中多数据源整合的总结 sql脚本在multipledatasources中,4个项目都是同一个sql脚本 multipledatasources: jpa-mybatis-多数据源的整合,其目的就是jpa用来增删改,mybatis用来查询,我认为没有比mybatis...

    Spring 2.0 开发参考手册

    9.7. 选择编程式事务管理还是声明式事务管理 9.8. 与特定应用服务器集成 9.8.1. BEA WebLogic 9.8.2. IBM WebSphere 9.9. 公共问题的解决方案 9.9.1. 对一个特定的 DataSource 使用错误的事务管理器 9.10. 更多...

    evaluator:用于成绩评估的简单 Web 应用程序

    作者:Marius Bogoevici、Tejas Mehta、Joshua Wilson 级别:中级技术:JSP、JPA、JSON、Spring、JUnit 总结:一个融合多种技术的例子目标产品:WFK 产品版本:EAP 6.1、EAP 6.2、WFK 2.6 来源: : 它是什么? 这...

    spring chm文档

    9.7. 选择编程式事务管理还是声明式事务管理 9.8. 与特定应用服务器集成 9.8.1. BEA WebLogic 9.8.2. IBM WebSphere 9.9. 公共问题的解决方案 9.9.1. 对一个特定的 DataSource 使用错误的事务管理器 9.10. 更多...

    Spring-Reference_zh_CN(Spring中文参考手册)

    9.7. 选择编程式事务管理还是声明式事务管理 9.8. 与特定应用服务器集成 9.8.1. BEA WebLogic 9.8.2. IBM WebSphere 9.9. 公共问题的解决方案 9.9.1. 对一个特定的 DataSource 使用错误的事务管理器 9.10. 更多的...

    Spring中文帮助文档

    9.7. 选择编程式事务管理还是声明式事务管理 9.8. 与特定应用服务器集成 9.8.1. IBM WebSphere 9.8.2. BEA WebLogic 9.8.3. Oracle OC4J 9.9. 常见问题的解决方法 9.9.1. 对一个特定的 DataSource 使用了错误...

    Spring API

    9.7. 选择编程式事务管理还是声明式事务管理 9.8. 与特定应用服务器集成 9.8.1. IBM WebSphere 9.8.2. BEA WebLogic 9.8.3. Oracle OC4J 9.9. 常见问题的解决方法 9.9.1. 对一个特定的 DataSource 使用了错误...

    Spring面试题

    6.spring对于主流的应用框架提供了集成支持,如hibernate,JPA,Struts等 7.spring属于低侵入式设计,代码的污染极低 8.独立于各种应用服务器 9.spring的DI机制降低了业务对象替换的复杂性 10.Spring的高度开放...

    iuhyiuhkjh908u0980

    Ebean的特性如下: ============== l 使用标准的JPA annotation,即:@Entity, @OneToMany等。 l 提供Sessionless API,也就是说没有merge,flush等方法。(这也是Ebean与Hibernatet等ORM的重要区别之一) l 支持...

Global site tag (gtag.js) - Google Analytics