位于上海,服务全国!

位于上海,服务全国!

通过EJB管理事务

作者:admin 分类: 时间:2017-04-27 10:54:38 点击量:536

管理事务的底层支持是EJB容器提供的主要服务之一。 自从Java语言开始以来,EJB框架一直是强化管理事务和访问控制的方便方法。同样可以遵守底层容器的自动功能。 本文以简洁的方式突出重点探讨了EJB的事务管理概念。

企业竞技场事务
事务指向一个集体活动,一个单位执行或根本不执行。 因为个体的活动是至关重要的,并且涉及必须完成的整个活动; 否则可能导致“遗憾”的情况。

一个单元的活动诸如持久数据对象,验证信用卡的安全信息,发送电子邮件等活动的单位实际上是几个单独任务的集合。 此外,企业竞技场是一个多任务环境,其中两个或多个相同交织任务的事务可能导致操作状态不一致。 例如,一个事务已经更新了一个文件中的数据并即将提交;在此期间,另一个事务会干预并从文件中提取旧/错误的数据。 这种类型的问题在事务中是非常常见的,并且通常通过实现不同的锁定机制来解决。

ACID(原子性,一致性,隔离性,耐久性)属性给出了维护事务处理完整性的准则。 企业应用程序通常在多层架构中工作。 毫无疑问,任何可靠的数据库都能够将这些(ACID)属性保持在低级别。 但是,在中层,甚至在客户端层面的类似问题呢? 可能的解决方案是使用特殊的应用程序逻辑来处理它们。 EJB在此提供了动力,其的服务构建了应用逻辑。 另一种情况可能是数据库可能使用内置的并发控制通过悲观锁定来管理事务。 但是,如果另一层的应用程序开发人员选择使用不同的锁定策略来优化性能呢? 这就是EJB发挥其作用的地方。 事务管理API或JTA集中在提供企业级的事务管理服务,使程序员掌握更精细的控制权。

管理EJB事务的方式

EJB事务建立在JTA模型上。 事务上下文通常由会话bean和应用程序客户端提供。 在这种情况下执行事务性服务。 这些服务就像创建,更新,检索和删除实体;调用Web服务;执行JDBC操作;等等。 事务管理器由EJB容器提供,但真正的功能在于由EJB提供的元数据形式的声明性服务。 此声明性元数据提供了积极参与事务过程的机会。 程序员可以通过使用这些元数据标签来操纵事务处理,而不是重新发明复杂的程序逻辑。 元数据标签控制在企业框架中业务方法的事务行为。

EJB向JTA和非JTA事务提供广泛的支持。 非JTA(本地资源)事务通常基于限于单个资源管理器的事务类型被选择,这是我们通常在创建数据库连接时可以看到的。 这特别优化了性能,因为在这种情况下,维护分布式事务监视器的开销显然不存在。

来自persistence.xml的基于JTA的JPA事务配置代码片段

<persistence-unit name="LibraryPU" transaction-type="JTA">
   <jta-data-source>java:app/library_jndi</jta-data-source>
   <exclude-unlisted-classes>false</exclude-unlisted-classes>
   <properties>
      <property
         name="javax.persistence.schema-generation.database.action"
         value="create"/>
   </properties>
</persistence-unit>
来自persistence.xml非JTA(本地资源)基于JPA事务配置的代码片段

<persistence-unit name="LibraryPU-JSE" transaction-type="RESOURCE_LOCAL">
   <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
   <exclude-unlisted-classes>false</exclude-unlisted-classes>
   <properties>
      <property
         name="javax.persistence.schema-generation.database.action"
         value="create"/>
      <property name="javax.persistence.jdbc.driver"
         value="com.mysql.jdbc.Driver"/>
      <property name="javax.persistence.jdbc.url"
         value="jdbc:mysql://localhost:3306/testdb?zeroDateTimeBehavior=
                convertToNull"/>
      <property name="javax.persistence.jdbc.user"
                value="root"/>
      <property name="javax.persistence.jdbc.password"
         value="secret"/>
      <property name="eclipselink.logging.level"
         value="FINER"/>
   </properties>
</persistence-unit>

开发人员可能通过利用容器管理的事务来选择更为被动地参与事务过程。 在默认情况下,容器管理的事务控制事务处理可缓解开发人员编写复杂代码。

通过积极参与事务过程,我们的意思是bean管理的事务,通过明确处理诸如开始事务,提交和回滚事件之类的活动来控制事务边界。

然而,也可以在单个应用程序中利用两个模型。 在这种情况下,EJB定义采取事务划分的决定性立场,无论是使用容器管理还是由Bean管理的事务模型。 例如,事务模型与JPA实体一起使用的决定由persistence.xml文件中提供的配置决定。

容器管理事务
默认情况下,容器管理的事务提供内置的机制来处理事务服务。 会话bean和消息驱动的bean可以使用这些服务。 给定事务指令使用注释或XML配置来指定事务感知方法。 EJB容器根据提供的指令执行事务操作。 这些指令定义了事务性划分,例如开始事务,挂起,重用现有事务,或者在调用该方法时提交。

当对事务方法进行调用时,EJB容器会干预并将规则应用于会话bean的方法边界。 默认情况下,容器检查当前事务上下文是否与当前正在运行的任何线程相关联。 如果发现,事务控制被赋予调用的方法;否则,在执行该方法之前开始新的事务。

但是,容器管理的事务划分默认行为可以通过使用@TransactionAttribute注释来装饰该方法进而重写。 属性值在javax.ejb.TransactionAttributeType枚举中被定义。

Enum Constants Description (*)
MANDATORY 如果客户端在其与事务上下文相关联时调用企业bean的方法,容器将在客户端的事务上下文中调用企业bean的方法。
NEVER 客户端需要调用没有事务上下文;否则抛出异常。
NOT_SUPPORTED 容器调用企业bean方法,其的事务属性为未指定事务上下文的NOT_SUPPORTED。
REQUIRED 如果客户端在其与事务上下文相关联时调用企业bean的方法,容器将在客户端的事务上下文中调用企业bean的方法。
REQUIRES_NEW 容器必须调用一个企业bean方法,其的事务属性被设置为具有新事务上下文的REQUIRES_NEW。
SUPPORTS 如果客户端使用事务上下文进行调用,则容器将执行与REQUIRED情况所述相同的步骤。

以下是通过注释在会话Bean方法上指定事务行为的示例。 这将覆盖默认行为,且其由容器管理事务模型指定。

@TransactionAttribute(value =
   TransactionAttributeType.SUPPORTS)
public BookOrder createBookOrder()
      throws Exception {

   // ...transaction critical code

}
容器管理的事务
我们可以看到,容器管理的事务模型自动处理事务管理的复杂事项。 在大多数情况下,开发人员不需要太多用它。 但是,它有自己的局限性。 在某些情况下,这种模式的划分粒度是不够的。 一个主要的缺陷是一个方法只能包含在一个事务中。 假设客户端希望在会话bean上调用多个方法,其中的每个方法完成执行而不提交。 客户端有两个选项来控制事务的划分。 首先,通过自定义容器管理的事务实例化自己的事务,或者通过使用事务资源,通过EJB上下文调用。 一个bean管理的事务完全执行第二个。

一个bean管理的事务提供了明确处理事务事件划分的能力。 我们可以通过注释@TransactionManagement(TransactionManagementType.BEAN)来关闭容器管理的事务服务默认行为,或者我们可以在名为ejb-jar.xml的文件中指定等效的元数据。 但是,使用bean管理的事务模型并不意味着容器将停止其的支持。 而是通过bean的EJBContext对象(如SessionContext)可用的UserTransaction对象来提供支持。

因此,两个模型之间的基本区别在于,在容器管理的事务模型中,隐含地建立事务划分,而bean管理的事务模型是明确的。

以下的代码示例(代码片段),提供了一个构思,以展现在代码中的样式。

@Stateless
@TransactionManagement(value=TransactionManagementType.BEAN)
public class BookOrderDaoBean {

   @Resource
   SessionContext sessionContext;

   @PersistenceContext
   private EntityManager entityManager;

   private final UserTransaction userTransaction=
      sessionContext.getUserTransaction();

   public void saveMultipleOrder(List<BookOrder> list){

      // ...
      for(BookOrder b: list){
         try{
            userTransaction.begin();
            entityManager.persist(b);
            userTransaction.commit();
         }catch(Exception e){
            try {
               userTransaction.rollback();
            } catch (Exception ex) {
               Logger.getLogger(BookOrderDaoBean.class
               .getName())
               .log(Level.SEVERE, null, ex);
            }
         }
      }
   }

   // ...

}
结论
容器管理的事务模型强制隐式提交行为,且是通过方法调用而建立事务的初始化来完成,并在其执行结束时终止。 开发人员摆脱了处理复杂性事项的负担。 但是,容器可能会失去对事务控制的跟踪,当其由进程堆栈弹出时。 Bean管理的事务更加灵活,具有明确的提交模型。 开发人员控制事务的划界。 因此,必须确保没有悬挂事务。 这是灵活性的代价。