首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件开发 >

JTA 深度历险 – 原理与兑现

2013-05-02 
JTA 深度历险 – 原理与实现简介:?在 J2EE 应用中,事务是一个不可或缺的组件模型,它保证了用户操作的 ACID(

JTA 深度历险 – 原理与实现

简介:?在 J2EE 应用中,事务是一个不可或缺的组件模型,它保证了用户操作的 ACID(即原子、一致、隔离、持久)属性。对于只操作单一数据源的应用,可以通过本地资源接口实现事务管理;对于跨数据源(例如多个数据库,或者数据库 与 JMS)的大型应用,则必须使用全局事务 JTA (Java Transaction API)。JTA 为 J2EE 平台提供了分布式事务服务,它隔离了事务与底层的资源,实现了透明的事务管理方式。本文将深入探寻 JTA 的体系架构,并通过详细的代码介绍其实现机制。

利用 JTA 处理事务

什么是事务处理

事务是计算机应用中不可或缺的组件模型,它保证了用户操作的原子性 ( Atomicity )、一致性 ( Consistency )、隔离性 ( Isolation ) 和持久性 ( Durabilily )。关于事务最经典的示例莫过于信用卡转账:将用户 A 账户中的 500 元人民币转移到用户 B 的账户中,其操作流程如下

1. 将 A 账户中的金额减少 500

2. 将 B 账户中的金额增加 500

这两个操作必须保正 ACID 的事务属性:即要么全部成功,要么全部失败;假若没有事务保障,用户的账号金额将可能发生问题:

假如第一步操作成功而第二步失败,那么用户 A 账户中的金额将就减少 500 元而用户 B 的账号却没有任何增加(不翼而飞);同样如果第一步出错 而第二步成功,那么用户 A 的账户金额不变而用户 B 的账号将增加 500 元(凭空而生)。上述任何一种错误都会产生严重的数据不一致问题,事务的缺失对于一个稳定的生产系统是不可接受的。

J2EE 事务处理方式

1. 本地事务:紧密依赖于底层资源管理器(例如数据库连接 ),事务处理局限在当前事务资源内。此种事务处理方式不存在对应用服务器的依赖,因而部署灵活却无法支持多数据源的分布式事务。在数据库连接中使用本地事务示例如下:

清单 1. 本地事务处理实例

开发人员使用开发人员接口,实现应用程序对全局事务的支持;各提供商(数据库,JMS 等)依据提供商接口的规范提供事务资源管理功能;事务管理器( TransactionManager )将应用对分布式事务的使用映射到实际的事务资源并在事务资源间进行协调与控制。 下面,本文将对包括 UserTransaction、Transaction 和 TransactionManager 在内的三个主要接口以及其定义的方法进行介绍。

面向开发人员的接口为 UserTransaction (使用方法如上例所示),开发人员通常只使用此接口实现 JTA 事务管理,其定义了如下的方法:

●begin()- 开始一个分布式事务,(在后台 TransactionManager 会创建一个 Transaction 事务对象并把此对象通过 ThreadLocale 关联到当前线程上 )

●commit()- 提交事务(在后台 TransactionManager 会从当前线程下取出事务对象并把此对象所代表的事务提交)

●rollback()- 回滚事务(在后台 TransactionManager 会从当前线程下取出事务对象并把此对象所代表的事务回滚)

●getStatus()- 返回关联到当前线程的分布式事务的状态 (Status 对象里边定义了所有的事务状态,感兴趣的读者可以参考 API 文档 )

●setRollbackOnly()- 标识关联到当前线程的分布式事务将被回滚

面向提供商的实现接口主要涉及到 TransactionManager 和 Transaction 两个对象

Transaction 代表了一个物理意义上的事务,在开发人员调用 UserTransaction.begin() 方法时 TransactionManager 会创建一个 Transaction 事务对象(标志着事务的开始)并把此对象通过 ThreadLocale 关联到当前线程。UserTransaction 接口中的 commit()、rollback(),getStatus() 等方法都将最终委托给 Transaction 类的对应方法执行。Transaction 接口定义了如下的方法:

●commit()- 协调不同的事务资源共同完成事务的提交

●rollback()- 协调不同的事务资源共同完成事务的回滚

●setRollbackOnly()- 标识关联到当前线程的分布式事务将被回滚

●getStatus()- 返回关联到当前线程的分布式事务的状态

●enListResource(XAResource xaRes, int flag)- 将事务资源加入到当前的事务中(在上述示例中,在对数据库 A 操作时 其所代表的事务资源将被关联到当前事务中,同样,在对数据库 B 操作时其所代表的事务资源也将被关联到当前事务中)

●delistResourc(XAResource xaRes, int flag)- 将事务资源从当前事务中删除

●registerSynchronization(Synchronization sync)- 回调接口,Hibernate 等 ORM 工具都有自己的事务控制机制来保证事务, 但同时它们还需要一种回调机制以便在事务完成时得到通知从而触发一些处理工作,如清除缓存等。这就涉及到了 Transaction 的回调接口 registerSynchronization。工具可以通过此接口将回调程序注入到事务中,当事务成功提交后,回调程序将被激活。

TransactionManager 本身并不承担实际的事务处理功能,它更多的是充当用户接口和实现接口之间的桥梁。下面列出了 TransactionManager 中定义的方法,可以看到此接口中的大部分事务方法与 UserTransaction 和 Transaction 相同。 在开发人员调用 UserTransaction.begin() 方法时 TransactionManager 会创建一个 Transaction 事务对象(标志着事务的开始)并把此对象通过 ThreadLocale 关联到当前线程上;同样 UserTransaction.commit() 会调用 TransactionManager.commit(), 方法将从当前线程下取出事务对象 Transaction 并把此对象所代表的事务提交, 即调用 Transaction.commit()

●begin()- 开始事务

●commit()- 提交事务

●rollback()- 回滚事务

●getStatus()- 返回当前事务状态

●setRollbackOnly()

●getTransaction()- 返回关联到当前线程的事务

●setTransactionTimeout(int seconds)- 设置事务超时时间

●resume(Transaction tobj)- 继续当前线程关联的事务

●suspend()- 挂起当前线程关联的事务

在系统开发过程中会遇到需要将事务资源暂时排除的操作,此时就需要调用 suspend() 方法将当前的事务挂起:在此方法后面所做的任何操作将不会被包括在事务中,在非事务性操作完成后调用 resume()以继续事务(注: 要进行此操作需要获得 TransactionManager 对象, 其获得方式在不同的 J2EE 应用服务器上是不一样的)

下面将通过具体的代码向读者介绍 JTA 实现原理。下图列出了示例实现中涉及到的 Java 类,其中 UserTransactionImpl 实现了 UserTransaction 接口,TransactionManagerImpl 实现了 TransactionManager 接口,TransactionImpl 实现了 Transaction 接口

图 2. JTA 实现类图

JTA 深度历险 – 原理与兑现

清单 3. 开始事务 – UserTransactionImpl implenments UserTransaction

应用程序从支持分布式事务的数据源获得的数据库连接是 XAConnection 接口的实现,而由此数据库连接创建的会话(Statement)也为了支持分布式事务而增加了功能,如下代码所示:

清单 8. JTA 事务资源处理

123456789101112131415161718public void commit() throws RollbackException, HeuristicMixedException,?????????????HeuristicRollbackException, SecurityException,?????????????IllegalStateException, SystemException { ??????????????// 得到当前事务中的所有事务资源????????????????List<XAResource> list = getAllEnlistedResouces(); ??????????????// 通知所有的事务资源管理器,准备提交事务????????????????????????// 对于生产级别的实现,此处需要进行额外处理以处理某些资源准备过程中出现的异常?????????????for(XAResource xa : list){?????????????????xa.prepare();?????????????} ??????????????// 所有事务性资源,提交事务?????????????for(XAResource xa : list){?????????????????xa.commit();?????????????}???????}

结束语

通过如上介绍相信读者对 JTA 的原理已经有所了解,本文中的示例代码都是理想情况下的假设实现。一款完善成熟的 JTA 事务实现需要考虑与处理的细节非常多,如性能(提交事务的时候使用多线程方式并发提交事务)、容错(网络,系统异常)等, 其成熟也需要经过较长时间的积累。感兴趣的读者可以阅读一些开源 JTA 实现以进一步深入学习。

热点排行