一般情况下,J2EE应用服务器支持JDBC事务、JTA事务、容器管理事务。这里讨论JTA和JDBC事务的区别。这2个是常用的DAO模式事务界定方式。
 JDBC 事务
 JDBC 事务是用 Connection 对象控制的。JDBC Connection 接口( java.sql.Connection )提供了两种事务模式:自动提交和手工提交。
★ 在jdbc中,事务操作缺省是自动提交。也就是说,一条对数据库的更新表达式代表一项事务操作,操作成功后,系统将自动调用commit()来提交,否则将调用rollback()来回滚。
★  在jdbc中,可以通过调用setAutoCommit(false)来禁止自动提交。之后就可以把多个数据库操作的表达式作为一个事务,在操作完成后调 用commit()来进行整体提交,倘若其中一个表达式操作失败,都不会执行到commit(),并且将产生响应的异常;此时就可以在异常捕获时调用 rollback()进行回滚。这样做可以保持多次更新操作后,相关数据的一致性,示例如下:

 try
try  {
{
 conn = DriverManager.getConnection
   conn = DriverManager.getConnection    
 conn.setAutoCommit(false);//禁止自动提交,设置回滚点
   conn.setAutoCommit(false);//禁止自动提交,设置回滚点
 stmt = conn.createStatement();
   stmt = conn.createStatement();
 stmt.executeUpdate(“alter table …”); //数据库更新操作1
   stmt.executeUpdate(“alter table …”); //数据库更新操作1
 stmt.executeUpdate(“insert into table …”); //数据库更新操作2
   stmt.executeUpdate(“insert into table …”); //数据库更新操作2
 conn.commit(); //事务提交
   conn.commit(); //事务提交

 }catch(Exception ex)
}catch(Exception ex)  {
{    
 ex.printStackTrace();
   ex.printStackTrace();

 try
   try  {
{
 conn.rollback(); //操作不成功则回滚
      conn.rollback(); //操作不成功则回滚

 }catch(Exception e)
   }catch(Exception e)  {
{
 e.printStackTrace();
       e.printStackTrace();
 }
   }
 }
}

JDBC 事务的一个缺点是事务的范围局限于一个数据库连接。一个 JDBC 事务不能跨越多个数据库。
 JTA事务
 JTA(Java Transaction API) 为 J2EE 平台提供了分布式事务服务。
 要用 JTA 进行事务界定,应用程序要调用 javax.transaction.UserTransaction 接口中的方法。例如:
 utx.begin();
      utx.begin();
 //
      // 
 DataSource ds = obtainXADataSource();
      DataSource ds = obtainXADataSource();
 Connection conn = ds.getConnection();
      Connection conn = ds.getConnection();
 pstmt = conn.prepareStatement("UPDATE MOVIES
      pstmt = conn.prepareStatement("UPDATE MOVIES  ");
");
 pstmt.setString(1, "Spinal Tap");
      pstmt.setString(1, "Spinal Tap");
 pstmt.executeUpdate();
      pstmt.executeUpdate();
 //
      // 
 utx.commit();
      utx.commit();

“用 JTA 界定事务,那么就需要有一个实现 javax.sql.XADataSource 、 javax.sql.XAConnection 和 javax.sql.XAResource 接口的 JDBC 驱动程序。一个实现了这些接口的驱动程序将可以参与 JTA 事务。一个 XADataSource 对象就是一个 XAConnection 对象的工厂。 XAConnection s 是参与 JTA 事务的 JDBC 连接。”
 要使用JTA事务,必须使用XADataSource来产生数据库连接,产生的连接为一个XA连接。
 XA连接(javax.sql.XAConnection)和非XA(java.sql.Connection)连接的区别在于:XA可以参与JTA的事务,而且不支持自动提交。
 Note:
 Note:
 Oracle, Sybase, DB2, SQL Server等大型数据库才支持XA, 支持分布事务。
Oracle, Sybase, DB2, SQL Server等大型数据库才支持XA, 支持分布事务。 
 My SQL 连本地都支持不好,更别说分布事务了。
My SQL 连本地都支持不好,更别说分布事务了。
JTA方式的实现过程:
   用XADataSource产生的XAConnection它扩展了一个getXAResource()方法,事务通过这个方法把它加入到事务容器中进行 管理.对于调用者来说,根本看不到事务是如果管理的,你只要声明开始事务,告诉容器我下面的操作要求事务参与了,最后告诉事务说到这儿可以提交或回滚了, 别的都是黑箱操作。
 在使用JTA之前,你必须首先实现一个Xid类用来标识事务(在普通情况下这将由事务管理程序来处理)。Xid包含三个元素:formatID、gtrid(全局事务标识符)和bqual(分支修饰词标识符)。
 下面的例子说明Xid的实现:
 import javax.transaction.xa.*;
import javax.transaction.xa.*; 

 public class MyXid implements Xid
public class MyXid implements Xid  {
{ 
 protected int formatId;
   protected int formatId; 
 protected byte gtrid[];
   protected byte gtrid[]; 
 protected byte bqual[];
   protected byte bqual[]; 

 public MyXid()
   public MyXid()  {
{ 
 }
   } 

 public MyXid(int formatId, byte gtrid[], byte bqual[])
   public MyXid(int formatId, byte gtrid[], byte bqual[])  {
{ 
 this.formatId = formatId;
    this.formatId = formatId; 
 this.gtrid = gtrid;
    this.gtrid = gtrid; 
 this.bqual = bqual;
    this.bqual = bqual; 
 }
   } 


 public int getFormatId()
 public int getFormatId()  {
{ 
 return formatId;
    return formatId; 
 }
    } 


 public byte[] getBranchQualifier()
 public byte[] getBranchQualifier()  {
{ 
 return bqual;
    return bqual; 
 }
    } 


 public byte[] getGlobalTransactionId()
 public byte[] getGlobalTransactionId()  {
{ 
 return gtrid;
    return gtrid; 
 }
    } 
 }
}

其次,你需要创建一个你要使用的数据库的数据源:

 public DataSource getDataSource() throws SQLException
public DataSource getDataSource() throws SQLException  {
{ 
 SQLServerDataSource xaDS = new
 SQLServerDataSource xaDS = new 
 com.merant.datadirect.jdbcx.sqlserver.SQLServerDataSource();
 com.merant.datadirect.jdbcx.sqlserver.SQLServerDataSource(); 
 xaDS.setDataSourceName("SQLServer");
 xaDS.setDataSourceName("SQLServer"); 
 xaDS.setServerName("server");
 xaDS.setServerName("server"); 
 xaDS.setPortNumber(1433);
 xaDS.setPortNumber(1433); 
 xaDS.setSelectMethod("cursor");
 xaDS.setSelectMethod("cursor"); 
 return xaDS;
 return xaDS; 
 }
} 
这个例子是用“两步提交协议”来提交一个事务分支:

 XADataSource xaDS;
XADataSource xaDS; 
 XAConnection xaCon;
XAConnection xaCon; 
 XAResource xaRes;
XAResource xaRes; 
 Xid xid;
Xid xid; 
 Connection con;
Connection con; 
 Statement stmt;
Statement stmt; 
 int ret;
int ret; 
 xaDS = getDataSource();
xaDS = getDataSource(); 
 xaCon = xaDS.getXAConnection("jdbc_user", "jdbc_password");
xaCon = xaDS.getXAConnection("jdbc_user", "jdbc_password"); 
 xaRes = xaCon.getXAResource();
xaRes = xaCon.getXAResource(); 
 con = xaCon.getConnection();
con = xaCon.getConnection(); 
 stmt = con.createStatement();
stmt = con.createStatement(); 

 xid = new MyXid(100, new byte[]
xid = new MyXid(100, new byte[] {0x01}, new byte[]
{0x01}, new byte[] {0x02});
{0x02}); 

 try
try  {
{ 
 xaRes.start(xid, XAResource.TMNOFLAGS);
 xaRes.start(xid, XAResource.TMNOFLAGS); 
 stmt.executeUpdate("insert into test_table values (100)");
 stmt.executeUpdate("insert into test_table values (100)"); 
 xaRes.end(xid, XAResource.TMSUCCESS);
 xaRes.end(xid, XAResource.TMSUCCESS); 
 ret = xaRes.prepare(xid);
 ret = xaRes.prepare(xid); 

 if (ret == XAResource.XA_OK)
 if (ret == XAResource.XA_OK)  {
{ 
 xaRes.commit(xid, false);
  xaRes.commit(xid, false); 
 }
 } 
 }
} 

 catch (XAException e)
catch (XAException e)  {
{ 
 e.printStackTrace();
 e.printStackTrace(); 
 }
} 

 finally
finally  {
{ 
 stmt.close();
 stmt.close(); 
 con.close();
 con.close(); 
 xaCon.close();
 xaCon.close(); 
 }
}
 两阶段提交(Two-Phase-Commit)协议
两阶段提交(Two-Phase-Commit)协议

 首先,两阶段提交(Two-Phase-Commit)事务的启动与常规的单阶段提交(One-Phase-Commit)事务类似。接着,应用程序/客 户机对该两阶段提交(Two-Phase-Commit)操作中所涉及的所有数据库执行其修改工作。现在,在最终提交该事务之前,客户机通知参与的数据库准备提交(第 1 阶段)。如果客户机从数据库收到一条“okay”,就发出命令向数据库提交该事务(第 2 阶段)。最后分布式事务(Distributed Transaction)结束。
首先,两阶段提交(Two-Phase-Commit)事务的启动与常规的单阶段提交(One-Phase-Commit)事务类似。接着,应用程序/客 户机对该两阶段提交(Two-Phase-Commit)操作中所涉及的所有数据库执行其修改工作。现在,在最终提交该事务之前,客户机通知参与的数据库准备提交(第 1 阶段)。如果客户机从数据库收到一条“okay”,就发出命令向数据库提交该事务(第 2 阶段)。最后分布式事务(Distributed Transaction)结束。

在该应用程序中,如果一个事务分支报告了错误,您就要负责进行错误处理。但是“两阶段提交协议 简介”小节中提到仍然存在一个问题,那就是如果第 2 阶段中一个事务分支发生故障,该怎么办呢?
如果再次查看程序代码,您可以看到在“第 1 阶段”和“第 2 阶段”之间有一个很小的时间间隔。在这一时间间隔中,出于某种理由,其中某一参与数据库可能崩溃。如果发生了,我们将陷入分布式事务已经部分提交的情形中。
假 定下列情形:在“第 1 阶段”之后,您从 DB2 和 IDS 数据库中都收到了“okay”。在下一步中,应用程序成功提交了 DB2 的事务分支。接着,应用程序通知 DB2 事务分支提交事务。现在,在应用程序可以通知 IDS 事务分支提交它这一部分之前,IDS 引擎由于断电发生崩溃。这就是一种部分提交全局事务的情形。您现在该怎么办呢?
在重启之后,DB2 和 IDS 都将尝试恢复打开的事务分支。该引擎等待来自应用程序的提示如何做。如果应用程序没有准备重新发送“第 2 阶段”的提交,该事务分支将被引擎所启动的试探性回滚中止。这是非常糟糕的,因为这将使该全局事务处于不一致状态。
一种解决方案是用一个小型应用程序连接引擎中打开的事务分支,并通知引擎提交或回滚这一打开的事务。如果您使用 IDS 作为后端,那么还有一个隐藏的 onmode 标志,允许您结束打开的事务分支。(onmode -Z xid)。
在 DB2 UDB 中,您可以发出 LIST INDOUBT TRANSACTIONS 来获得打开的 XA 事务的有关信息。您必须查看 DB2 Information Center 中的描述来解决该问题。
上面描述的情形是一个很好的例子,也是使用应用程序服务器(Application Server)或事务监控器(Transaction Monitor)的理由。在使用一个中间层服务器时,就由该服务器负责保持事情正常。