文章目录
JTA事务
介绍
什么是JTA事务
JTA:(Java Transaction API)Java事务API。是一个Java企业版的应用程序接口,在Java程序中,允许完成跨越多个XA资源的分布式事务。
隔离事务与底层的资源,实现透明的事务管理方式。
提供了跨数据库连接(或其他JTA资源)的事务管理能力。
JTA事务管理由JTA容器实现。
一个JTA事务可以有多个参与者。
只是提供了一个接口,并没有提供具体的实现,而是由 j2ee服务提供商 根据JTS(Java Transaction Service:Java 事务服务)规范提供的。
什么是XA规范
X/Open 组织(即现在的 Open Group )定义了分布式事务处理模型。 X/Open DTP 模型( 1994 )包括应用程序( AP )、事务管理器( TM )、资源管理器( RM )、通信资源管理器( CRM )四部分。
一般常见的事务管理器( TM )是交易中间件,例如JDBC或者hibernate提供的transactionmanager,常见的资源管理器( RM )是数据库,通常就是数据源,例如JDBC或第三方提供的datasource,常见的通信资源管理器( CRM )是消息中间件,如JMS。
常见的JTA实现
1、J2EE容器所提供的JTA实现(JBoss)
2、独立的JTA实现:这些实现可以应用在那些不使用J2EE应用服务器的环境里用以提供分布事事务保证。如Tomcat,Jetty以及普通的java应用。
- JOTM
- Atomikos
JTA的架构
JTA的架构包括事务管理器(Transaction Manager)和一个或多个支持 XA 协议的资源管理器 ( Resource Manager ) 两部分, 我们可以将资源管理器看做任意类型的持久化数据存储;事务管理器则承担着所有事务参与单元的协调与控制。 根据所面向对象的不同,我们可以将 JTA 的事务管理器和资源管理器理解为两个方面:面向开发人员的使用接口(事务管理器)和面向服务提供商的实现接口(资源管理器)。其中开发接口的主要部分即为 UserTransaction 对象,开发人员通过此接口在信息系统中实现分布式事务;而实现接口则用来规范提供商(如数据库连接提供商)所提供的事务服务,它约定了事务的资源管理功能,使得 JTA 可以在异构事务资源之间执行协同沟通。
- 以数据库为例,IBM 公司提供了实现分布式事务的数据库驱动程序,Oracle 也提供了实现分布式事务的数据库驱动程序, 在同时使用 DB2 和 Oracle 两种数据库连接时, JTA 即可以根据约定的接口协调者两种事务资源从而实现分布式事务。
正是基于统一规范的不同实现使得 JTA 可以协调与控制不同数据库或者 JMS 厂商的事务资源。
UserTransaction接口(面向开发人员的接口)
开发人员通常只使用此接口实现JTA事务管理。
- 定义了如下方法:
- begin():开始一个分布式事务。(在后台 TransactionManager 会创建一个 Transaction 事务对象并把此对象通过 ThreadLocale 关联到当前线程上 )
- commit():提交事务。(在后台 TransactionManager 会从当前线程下取出事务对象并把此对象所代表的事务提交)
- rollback():回滚事务。(在后台 TransactionManager 会从当前线程下取出事务对象并把此对象所代表的事务回滚)
- getStatus():返回关联到当前线程的分布式事务的状态 。(Status 对象里边定义了所有的事务状态,感兴趣的读者可以参考 API 文档 )
- setRollbackOnly():标识关联到当前线程的分布式事务将被回滚
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接口(面向提供商的接口)
本身并不承担实际的事务处理功能,它更多的是充当用户接口和实现接口之间的桥梁。
在开发人员调用 UserTransaction.begin() 方法时 TransactionManager 会创建一个 Transaction 事务对象(标志着事务的开始)并把此对象通过 ThreadLocale 关联到当前线程上;同样 UserTransaction.commit() 会调用 TransactionManager.commit(), 方法将从当前线程下取出事务对象 Transaction 并把此对象所代表的事务提交, 即调用 Transaction.commit()。
TransactionManager 中定义的方法如下:
- begin():开始事务。
- commit():提交事务。
- rollback():回滚事务。
- getStatus():返回当前事务状态。
- setRollbackOnly():标识关联到当前线程的分布式事务将被回滚。
- getTransaction():返回关联到当前线程的事务。
- setTransactionTimeout(int seconds):设置事务超时时间。
- resume(Transaction tobj):继续当前线程关联的事务。
- suspend():挂起当前线程关联的事务。在系统开发过程中会遇到需要将事务资源暂时排除的操作,此时就需要调用 suspend() 方法将当前的事务挂起:在此方法后面所做的任何操作将不会被包括在事务中,在非事务性操作完成后调用 resume()以继续事务(注: 要进行此操作需要获得 TransactionManager 对象, 其获得方式在不同的 J2EE 应用服务器上是不一样的)
实例
JTA事务处理过程
- 实现类图
- 开始事务:UserTransactionImpl implenments UserTransaction
public void begin() throws NotSupportedException, SystemException {
// 将开始事务的操作委托给 TransactionManagerImpl
TransactionManagerImpl.singleton().begin();
}
- 开始事务:TransactionManagerImpl implements TransactionManager
// 此处 transactionHolder 用于将 Transaction 所代表的事务对象关联到线程上
private static ThreadLocal<TransactionImpl> transactionHolder = new ThreadLocal<TransactionImpl>();
//TransacationMananger 必须维护一个全局对象,因此使用单实例模式实现
private static TransactionManagerImpl singleton = new TransactionManagerImpl();
private TransactionManagerImpl(){
}
public static TransactionManagerImpl singleton(){
return singleton;
}
public void begin() throws NotSupportedException, SystemException {
//XidImpl 实现了 Xid 接口,其作用是唯一标识一个事务
XidImpl xid = new XidImpl();
// 创建事务对象,并将对象关联到线程
TransactionImpl tx = new TransactionImpl(xid);
transactionHolder.set(tx);
}
Transaction 接口上没有定义 begin 方法的原因:Transaction 对象本身就代表了一个事务,在它被创建的时候就表明事务已经开始,因此也就不需要额外定义 begin() 方法了。
- 提交事务:UserTransactionImpl implenments UserTransaction
public void commit() throws RollbackException, HeuristicMixedException,
HeuristicRollbackException, SecurityException,
IllegalStateException, SystemException {
// 检查是否是 Roll back only 事务,如果是回滚事务
if(rollBackOnly){
rollback();
return;
} else {
// 将提交事务的操作委托给 TransactionManagerImpl
TransactionManagerImpl.singleton().commit();
}
}
- 提交事务:TransactionManagerImpl implenments TransactionManager
public void commit() throws RollbackException, HeuristicMixedException,
HeuristicRollbackException, SecurityException,
IllegalStateException, SystemException {
// 取得当前事务所关联的事务并通过其 commit 方法提交
TransactionImpl tx = transactionHolder.get();
tx.commit();
}
同理,rollback、getStatus、setRollbackOnly 等方法也采用了有commit相同的方式实现。
UserTransaction 对象不会对事务进行任何控制, 所有的事务方法都是通过 TransactionManager 传递到实际的事务资源即 Transaction 对象上。
本地事务处理实例
紧密依赖于底层资源管理器(例如数据库连接),事务处理局限在当前事务资源内。
这种事务处理方式不存在对应用服务器的依赖,因而部署灵活,但无法支持多数据源的分布式事务。
public void transferAccount() {
Connection conn = null;
Statement stmt = null;
try{
conn = getDataSource().getConnection();
// 将自动提交设置为 false,
//若设置为 true 则数据库将会把每一次数据更新认定为一个事务并自动提交
conn.setAutoCommit(false);
stmt = conn.createStatement();
// 将 A 账户中的金额减少 500
stmt.execute("\update t_account set amount = amount - 500 where account_id = 'A'");
// 将 B 账户中的金额增加 500
stmt.execute("\update t_account set amount = amount + 500 where account_id = 'B'");
// 提交事务
conn.commit();
// 事务提交:转账的两步操作同时成功
} catch(SQLException sqle){
try{
// 发生异常,回滚在本事务中的操做
conn.rollback();
// 事务回滚:转账的两步操作完全撤销
stmt.close();
conn.close();
}catch(Exception ignore){
}
sqle.printStackTrace();
}
}
JTA事务处理实例(分布式事务处理)
Java事务编程接口(JTA:Java Transaction API)和Java事务服务(JTS:Java Transaction Service)为J2EE平台提供了分布式事务服务。
分布式事务(Distributed Transaction)包括事务管理器(Transaction Manager)和一个或多个支持XA协议的资源管理器(Resource Manager)。其实可以将资源管理器看作任意类型的持久化数据存储;事务管理器承担着所有的事务,参与单元的协调和控制。
JTA事务有效的屏蔽了底层事务资源,使应用可以以透明的方式渗入到事务处理中。
JTA事务与本地事务相比,XA协议的系统开销大,在系统开发过程中应该慎重考虑是否需要分布式事务;真需要分布式事务来协调多个事务资源,就要实现和配置支持XA协议的事务资源,如:JMS、JDBC数据库连接池。
- JMS:Java消息服务(Java Message Service)。应用程序A发送一条消息到消息服务器的某个目得地(Destination),然后消息服务器把消息转发给应用程序B。因为应用程序A和应用程序B没有直接的代码关连,所以两者实现了解偶。支持两种消息传递模型:点对点(point-to-point:PTP)和发布/订阅(publish/subscribe:pub/sub)。
- JDBC数据库连接池:用户每次请求都需要向数据库获取连接,而数据库创建连接需要耗费较大的资源。所以可以设置一个连接池,在程序启动时就初始化一批连接,在程序*享,需要连接时从连接池获取,用完之后再还给连接池,这样就大大减少了连接的创建和销毁次数,提高程序的效率。如:Druid、DBCP、C3P0
public void transferAccount() {
UserTransaction userTx = null;
Connection connA = null;
Statement stmtA = null;
Connection connB = null;
Statement stmtB = null;
try{
// 获得 Transaction 管理对象
userTx = (UserTransaction)getContext().lookup("\
java:comp/UserTransaction");
// 从数据库 A 中取得数据库连接
connA = getDataSourceA().getConnection();
// 从数据库 B 中取得数据库连接
connB = getDataSourceB().getConnection();
// 启动事务
userTx.begin();
// 将 A 账户中的金额减少 500
stmtA = connA.createStatement();
stmtA.execute("
update t_account set amount = amount - 500 where account_id = 'A'");
// 将 B 账户中的金额增加 500
stmtB = connB.createStatement();
stmtB.execute("\update t_account set amount = amount + 500 where account_id = 'B'");
// 提交事务
userTx.commit();
// 事务提交:转账的两步操作同时成功(数据库 A 和数据库 B 中的数据被同时更新)
} catch(SQLException sqle){
try{
// 发生异常,回滚在本事务中的操纵
userTx.rollback();
// 事务回滚:转账的两步操作完全撤销
//( 数据库 A 和数据库 B 中的数据更新被同时撤销)
stmt.close();
conn.close();
...
}catch(Exception ignore){
}
sqle.printStackTrace();
} catch(Exception ne){
e.printStackTrace();
}
}
补充
JTA事务和JDBC事务的区别
JTA事务:
- JTA一般由应用服务器管理,可以跨数据库、跨资源进行事务管理。
- JTA事务的应用服务器要支持分布式事务,需要使用两阶段提交协议。
- JTA提供跨session的事务管理能力。
- JTA事务管理由JTA容器实现,对当前加入事务的众多Connection进行调度,实现事务性要求。
- JTA事务周期可以跨多个JDBC Connection生命周期。
JDBC事务:
- JDBC事务由Connnection管理,也就是说,事务管理实际上是在JDBC Connection 中实现。
- JDBC事务周期限于Connection的生命周期之类。
- 对于基于JDBC Transaction 的Hibernate 事务管理机制而言,事务管理在Session 所依托的JDBC Connection 中实现,事务周期限于Session的生命周期。
参考资料:
https://www.cnblogs.com/xingzc/p/5745631.html