JDBC事务

---恢复内容开始---php

ACID,是指在 数据库管理系统DBMS)中 事务所具备的四个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。
在数据库系统中,一个事务是指由一系列数据库操做组成的一个完整的逻辑过程。例如银行转账,从原帐户扣除金额,以及向目标帐户添加金额,这两个数据库操做的总和构成一个完整的逻辑过程,不可拆分。这个过程被称为一个事务,具备ACID特性。
原子性
整个事务中的全部操做,要么所有完成,要么所有不完成,不可能停滞在中间某个环节。事务在执行过程当中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务历来没有执行过同样。
一致性
在事务开始以前和事务结束之后,数据库的完整性约束没有被破坏。
隔离性
两个事务的执行是互不干扰的,一个事务不可能看到其余事务运行时,中间某一时刻的数据。
持久性
在事务完成之后,该事务所对数据库所做的更改便持久的保存在数据库之中,并不会被回滚。

Java事务的类型有三种:JDBC事务JTA(Java Transaction API)事务容器事务。 常见的容器事务如Spring事务,容器事务主要是J2EE应用服务器提供的,容器事务大可能是基于JTA完成,这是一个基于JNDI的,至关复杂的API实现。因此本文暂不讨论容器事务。本文主要介绍J2EE开发中两个比较基本的事务:JDBC事务JTA事务java

JDBC的一切行为包括事务是基于一个Connection的,在JDBC中是经过Connection对象进行事务管理。在JDBC中,经常使用的和事务相关的方法是: setAutoCommitcommitrollback等。sql

下面看一个简单的JDBC事务代码:数据库

public void JdbcTransfer() { 
    java.sql.Connection conn = null;
     try{ 
        conn = conn =DriverManager.getConnection("jdbc:oracle:thin:@host:1521:SID","username","userpwd");
         // 将自动提交设置为 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(); 
     } 
}

上面的代码实现了一个简单的转帐功能,经过事务来控制转帐操做,要么都提交,要么都回滚。服务器


JDBC事务的优缺点

JDBC为使用Java进行数据库的事务操做提供了最基本的支持。经过JDBC事务,咱们能够将多个SQL语句放到同一个事务中,保证其ACID特性。JDBC事务的主要优势就是API比较简单,能够实现最基本的事务操做,性能也相对较好。oracle

可是,JDBC事务有一个局限:一个 JDBC 事务不能跨越多个数据库!!!因此,若是涉及到多数据库的操做或者分布式场景,JDBC事务就无能为力了。框架

JTA事务

为何须要JTA

一般,JDBC事务就能够解决数据的一致性等问题,鉴于他用法相对简单,因此不少人关于Java中的事务只知道有JDBC事务,或者有人知道框架中的事务(好比Hibernate、Spring)等。可是,因为JDBC没法实现分布式事务,而现在的分布式场景愈来愈多,因此,JTA事务就应运而生。异步

若是,你在工做中没有遇到JDBC事务没法解决的场景,那么只能说你作的项目还都过小。拿电商网站来讲,咱们通常把一个电商网站横向拆分红商品模块、订单模块、购物车模块、消息模块、支付模块等。而后咱们把不一样的模块部署到不一样的机器上,各个模块之间经过远程服务调用(RPC)等方式进行通讯。以一个分布式的系统对外提供服务。分布式

一个支付流程就要和多个模块进行交互,每一个模块都部署在不一样的机器中,而且每一个模块操做的数据库都不一致,这时候就没法使用JDBC来管理事务。咱们看一段代码:性能

/** 支付订单处理 **/
@Transactional(rollbackFor = Exception.class)
public void completeOrder() {
    orderDao.update(); // 订单服务本地更新订单状态
    accountService.update(); // 调用资金帐户服务给资金账户加款
    pointService.update(); // 调用积分服务给积分账户增长积分
    accountingService.insert(); // 调用会计服务向会计系统写入会计原始凭证
    merchantNotifyService.notify(); // 调用商户通知服务向商户发送支付结果通知
}

上面的代码是一个简单的支付流程的操做,其中调用了五个服务,这五个服务都经过RPC的方式调用,请问使用JDBC如何保证事务一致性?我在方法中增长了@Transactional注解,可是因为采用调用了分布式服务,该事务并不能达到ACID的效果。

JTA事务比JDBC事务更强大。一个JTA事务能够有多个参与者,而一个JDBC事务则被限定在一个单一的数据库链接。下列任一个Java平台的组件均可以参与到一个JTA事务中:JDBC链接、JDO PersistenceManager 对象、JMS 队列、JMS 主题、企业JavaBeans(EJB)、一个用J2EE Connector Architecture 规范编译的资源分配器。

JTA的定义

Java事务API(Java Transaction API,简称JTA ) 是一个Java企业版 的应用程序接口,在Java环境中,容许完成跨越多个XA资源的分布式事务。

JTA和它的同胞Java事务服务(JTS;Java TransactionService),为J2EE平台提供了分布式事务服务。不过JTA只是提供了一个接口,并无提供具体的实现,而是由j2ee服务器提供商 根据JTS规范提供的,常见的JTA实现有如下几种:

  • 1.J2EE容器所提供的JTA实现(JBoss)
  • 2.独立的JTA实现:如JOTM,Atomikos.这些实现能够应用在那些不使用J2EE应用服务器的环境里用以提供分布事事务保证。如Tomcat,Jetty以及普通的java应用。

JTA里面提供了 java.transaction.UserTransaction ,里面定义了下面几个方法

begin:开启一个事务

commit:提交当前事务

rollback:回滚当前事务

setRollbackOnly:把当前事务标记为回滚

setTransactionTimeout:设置事务的事件,超过这个事件,就抛出异常,回滚事务

这里,值得注意的是,不是使用了UserTransaction就能把普通的JDBC操做直接转成JTA操做,JTA对DataSource、Connection和Resource 都是有要求的,只有符合XA规范,而且实现了XA规范的相关接口的类才能参与到JTA事务中来,关于XA规范,请看个人另一篇文章中有相关介绍。这里,提一句,目前主流的数据库都支持XA规范。

要想使用用 JTA 事务,那么就须要有一个实现 javax.sql.XADataSource 、javax.sql.XAConnection 和 javax.sql.XAResource 接口的 JDBC 驱动程序。一个实现了这些接口的驱动程序将能够参与 JTA 事务。一个 XADataSource 对象就是一个 XAConnection 对象的工厂。XAConnection 是参与 JTA 事务的 JDBC 链接。

要使用JTA事务,必须使用XADataSource来产生数据库链接,产生的链接为一个XA链接。

XA链接(javax.sql.XAConnection)和非XA(java.sql.Connection)链接的区别在于:XA能够参与JTA的事务,并且不支持自动提交。

public void JtaTransfer() { 
        javax.transaction.UserTransaction tx = null;
        java.sql.Connection conn = null;
         try{ 
             tx = (javax.transaction.UserTransaction) context.lookup("java:comp/UserTransaction");  //取得JTA事务,本例中是由Jboss容器管理
             javax.sql.DataSource ds = (javax.sql.DataSource) context.lookup("java:/XAOracleDS");  //取得数据库链接池,必须有支持XA的数据库、驱动程序  
             tx.begin();
            conn = ds.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'");

             // 提交事务
             tx.commit();
             // 事务提交:转帐的两步操做同时成功
         } catch(SQLException sqle){            
             try{ 
                 // 发生异常,回滚在本事务中的操作
              tx.rollback();
                 // 事务回滚:转帐的两步操做彻底撤销
                 stmt.close(); 
                 conn.close(); 
             }catch(Exception ignore){ 

             } 
             sqle.printStackTrace(); 
         } 
     }

 

上面的例子就是一个使用JTA事务的转帐操做,该操做相对依赖于J2EE容器,而且须要经过JNDI的方式获取UserTransactionConnection

 

标准的分布式事务

一个分布式事务(Distributed Transaction)包括一个事务管理器(transaction manager)和一个或多个资源管理器(resource manager)。一个资源管理器(resource manager)是任意类型的持久化数据存储。事务管理器(transaction manager)承担着全部事务参与单元者的相互通信的责任。

JTA的实现方式也是基于以上这些分布式事务参与者实现的,具体的关于JTA的实现细节不是本文的重点,感兴趣的同窗能够阅读JTA 深度历险 – 原理与实现

  • 看上面关于分布式事务的介绍是否是和2PC中的事务管理比较像?的却,2PC其实就是符合XA规范的事务管理器协调多个资源管理器的一种实现方式。 我以前有几篇文章关于2PC和3PC的,那几篇文章中介绍过度布式事务中的事务管理器是如何协调多个事务的统一提交或回滚的,后面我还会有几篇文章详细的介绍一下和分布式事务相关的内容,包括但不限于全局事务、DTP模型、柔性事务等。

JTA的优缺点

JTA的优势很明显,就是提供了分布式事务的解决方案,严格的ACID。可是,标准的JTA方式的事务管理在平常开发中并不经常使用,由于他有不少缺点:

  • 实现复杂
    • 一般状况下,JTA UserTransaction须要从JNDI获取。这意味着,若是咱们使用JTA,就须要同时使用JTA和JNDI。
  • JTA自己就是个笨重的API
  • 一般JTA只能在应用服务器环境下使用,所以使用JTA会限制代码的复用性。

总结

Java事务的类型有三种:JDBC事务JTA(Java Transaction API)事务容器事务,其中JDBC的事务操做用法比较简单,适合于处理同一个数据源的操做。JTA事务相对复杂,能够用于处理跨多个数据库的事务,是分布式事务的一种解决方案。

这里还要简单说一下,虽然JTA事务是Java提供的可用于分布式事务的一套API,可是不一样的J2EE平台的实现都不同,而且都不是很方便使用,因此,通常在项目中不太使用这种较为负责的API。如今业内比较经常使用的分布式事务解决方案主要有异步消息确保型、TCC、最大努力通知等。关于这几种分布式事务解决方案,我会在后面的文章中介绍。欢迎关注与交流。

相关文章
相关标签/搜索