Posted on 2006-05-22 15:53 
糊里糊涂 阅读(312) 
评论(0)  编辑  收藏  所属分类: 
Java 
			 
			
		 
		最近由于项目原因,底层数据库访问都必须使用JDBC来操作,为了能更好的实现事务,而且也便于将来移植到Ibatis上去,在作设计的时候参照Ibatis的Dao模式来设计dao,然后事务控制就必须得自己手工来实现了。并且一起也实现了事务得嵌套。主要依靠2个类来实现。 
    1,TransactionUtil类,负责开启事务,提交事务以及关闭事务。
    2,Transaction类,用来记录当前事务得状态以及数据库连接。
   
|  package com.orizone.oa.extra.service; import java.sql.Connection;import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
 import net.orizone.oa.common.ConnectionPoolBean; import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;
 import com.orizone.comm.util.BusinessException; public class TransactionUtil {
 private final static ThreadLocal local = new ThreadLocal();
 private static Log log = LogFactory.getLog(TransactionUtil.class);
 
 /**
 * 开启事务
 */
 public static void startTransaction()throws  BusinessException
 {
 Transaction tran = (Transaction)local.get();
 //判断此事务是否属于一个顶层事务。
 if(tran == null)
 {
 tran = new Transaction();
 //设置本地线程的connection
 Connection con = ConnectionPoolBean.getConnection();
 try
 {
 con.setAutoCommit(false);
 }catch(SQLException e)
 {
 e.printStackTrace();
 throw new BusinessException(e, "开启事务失败!");
 }
 tran.setConnection(con);
 tran.setCommitCount(0);
 tran.setTransCount(1);
 tran.setTransDeep(1);
 
 local.set(tran);
 }else
 {
 //事务已经开启,将嵌套层次深度加一,将事务次数加一
 tran.setTransCount(tran.getTransCount() + 1);
 tran.setTransDeep(tran.getTransDeep() + 1);
 }
 }
 
 /**
 * 提交事务
 *
 */
 public static void commitTransaction()throws  BusinessException
 {
 Transaction tran = (Transaction)local.get();
 //如果事务属于嵌套,则不提交数据,直接将层次数减一。
 if(tran.getTransDeep() > 1)
 {
 tran.setTransDeep(tran.getTransDeep() - 1);
 tran.setCommitCount(tran.getCommitCount() + 1);
 return;
 }
 
 Connection con = tran.getConnection();
 try
 {
 if(tran.hasFullExecute())
 {
 con.commit();
 }
 }catch(SQLException e)
 {
 log.error(e);
 throw new BusinessException(e, "提交事务失败!");
 }
 }
 
 /**
 * 结束事务
 *
 */
 public static void endTransaction()throws  BusinessException
 {
 Transaction tran = (Transaction)local.get();
 //如果事务属于嵌套,则不关闭连接,直接将层次数减一。
 if(tran.getTransDeep() > 1)
 {
 tran.setTransDeep(tran.getTransDeep() - 1);
 return;
 }
 
 //当前事务已经结束,清空ThreadLocal变量,防止下一次操作拿到已经关闭的Connection对象。
 local.set(null);
 Connection con = tran.getConnection();
 try
 {
 if(!tran.hasFullExecute())
 {
 con.rollback();
 }
 }catch(SQLException e)
 {
 log.error(e);
 throw new BusinessException(e, "事务回滚失败!");
 }finally
 {
 try
 {
 con.close();
 }catch(SQLException se)
 {
 log.error(se);
 throw new BusinessException(se, "关闭事务失败!");
 }
 }
 
 }
 
 /**
 * 获取当前事务的数据库连接。
 * @return
 */
 public static Connection getConnection()
 {
 Transaction tran = (Transaction)local.get();
 Connection  con = tran.getConnection();
 if(con == null)
 {
 con = ConnectionPoolBean.getConnection();
 }
 return con;
 }
 
 /**
 * 测试代码
 * @param args
 * @throws Exception
 */
 public static void main(String args[])throws Exception
 {
 test();
 test();
 }
 
 /**
 * 测试代码
 *
 */
 private static void test()
 {
 TransactionUtil.startTransaction();
 try
 {
 Connection con = TransactionUtil.getConnection();
 Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
 stmt.executeUpdate("INSERT INTO bb(bb) values('bb')");
 stmt.executeUpdate("INSERT INTO aa(aa) values('aa')");
 //if(true) throw new Exception("");
 TransactionUtil.commitTransaction();
 }catch(Exception e)
 {
 e.printStackTrace();
 //throw new BusinessException("");
 }finally
 {
 TransactionUtil.endTransaction();
 }
 }
 }
 | 
 
| package com.orizone.oa.extra.service; import java.sql.Connection; public class Transaction {
 //数据库连接对象
 private Connection connection;
 //事务次数
 private int transCount;
 //提交次数
 private int commitCount;
 //事务嵌套层次
 private int transDeep;
  int getCommitCount() {
 return commitCount;
 }
  void setCommitCount(int commitCount) {
 this.commitCount = commitCount;
 }
  Connection getConnection() {
 return connection;
 }
  void setConnection(Connection conn) {
 this.connection = conn;
 }
  public int getTransCount() {
 return transCount;
 }
  void setTransCount(int transCount) {
 this.transCount = transCount;
 }
  int getTransDeep() {
 return transDeep;
 }
  void setTransDeep(int transDeep){
 this.transDeep = transDeep;
 }
 
 /**
 * 判断事务是否完全提交。
 * 通过提交次数和事务次数来判断事务是否完全提交。
 * @return
 */
 boolean hasFullExecute()
 {
 return commitCount + 1 == transCount;
 }
 }
 | 
 
代码中出现的ConnectionPoolBean是用来负责获取数据库连接的类。整个思想就是,将一个Transaction相关信息(数据库连接对象,事务次数,提交次数以及事务深度)放入到当前线程的ThradLocal当中,后面的操作都是基于这个事务基础的,这样才能保证事务的原子性。在commit的时候会判断当前事务层次深度,如果为顶层,并且提交次数+1等于事务次数(说明事务是安全完整的执行了),才真正提交到数据库。如果不完整,则在endTrnasaction的时候会回滚整个事务。
虽然这样能够实现事务操作,但是无法实现跨数据库操作,要实现跨数据库的事务估计只能用JTA了。