最近由于项目原因,底层数据库访问都必须使用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了。