手工實(shí)現(xiàn)JDBC事務(wù)管理。
[ 2006-4-29 12:41:06 | By: foxty ]
最近由于項(xiàng)目原因,底層數(shù)據(jù)庫訪問都必須使用JDBC來操作,為了能更好的實(shí)現(xiàn)事務(wù),而且也便于將來移植到Ibatis上去,在作設(shè)計(jì)的時(shí)候參照Ibatis的Dao模式來設(shè)計(jì)dao,然后事務(wù)控制就必須得自己手工來實(shí)現(xiàn)了。并且一起也實(shí)現(xiàn)了事務(wù)得嵌套。主要依靠2個(gè)類來實(shí)現(xiàn)。
1,TransactionUtil類,負(fù)責(zé)開啟事務(wù),提交事務(wù)以及關(guān)閉事務(wù)。
2,Transaction類,用來記錄當(dāng)前事務(wù)得狀態(tài)以及數(shù)據(jù)庫連接。
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); /** * 開啟事務(wù) */ public static void startTransaction()throws BusinessException { Transaction tran = (Transaction)local.get(); //判斷此事務(wù)是否屬于一個(gè)頂層事務(wù)。 if(tran == null) { tran = new Transaction(); //設(shè)置本地線程的connection Connection con = ConnectionPoolBean.getConnection(); try { con.setAutoCommit(false); }catch(SQLException e) { e.printStackTrace(); throw new BusinessException(e, "開啟事務(wù)失?。?); } tran.setConnection(con); tran.setCommitCount(0); tran.setTransCount(1); tran.setTransDeep(1); local.set(tran); }else { //事務(wù)已經(jīng)開啟,將嵌套層次深度加一,將事務(wù)次數(shù)加一 tran.setTransCount(tran.getTransCount() + 1); tran.setTransDeep(tran.getTransDeep() + 1); } } /** * 提交事務(wù) * */ public static void commitTransaction()throws BusinessException { Transaction tran = (Transaction)local.get(); //如果事務(wù)屬于嵌套,則不提交數(shù)據(jù),直接將層次數(shù)減一。 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, "提交事務(wù)失敗!"); } } /** * 結(jié)束事務(wù) * */ public static void endTransaction()throws BusinessException { Transaction tran = (Transaction)local.get(); //如果事務(wù)屬于嵌套,則不關(guān)閉連接,直接將層次數(shù)減一。 if(tran.getTransDeep() > 1) { tran.setTransDeep(tran.getTransDeep() - 1); return; } //當(dāng)前事務(wù)已經(jīng)結(jié)束,清空ThreadLocal變量,防止下一次操作拿到已經(jīng)關(guān)閉的Connection對象。 local.set(null); Connection con = tran.getConnection(); try { if(!tran.hasFullExecute()) { con.rollback(); } }catch(SQLException e) { log.error(e); throw new BusinessException(e, "事務(wù)回滾失敗!"); }finally { try { con.close(); }catch(SQLException se) { log.error(se); throw new BusinessException(se, "關(guān)閉事務(wù)失敗!"); } } } /** * 獲取當(dāng)前事務(wù)的數(shù)據(jù)庫連接。 * @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(‘a(chǎn)a‘)"); //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 { //數(shù)據(jù)庫連接對象 private Connection connection; //事務(wù)次數(shù) private int transCount; //提交次數(shù) private int commitCount; //事務(wù)嵌套層次 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; } /** * 判斷事務(wù)是否完全提交。 * 通過提交次數(shù)和事務(wù)次數(shù)來判斷事務(wù)是否完全提交。 * @return */ boolean hasFullExecute() { return commitCount + 1 == transCount; }
}
|
代碼中出現(xiàn)的ConnectionPoolBean是用來負(fù)責(zé)獲取數(shù)據(jù)庫連接的類。整個(gè)思想就是,將一個(gè)Transaction相關(guān)信息(數(shù)據(jù)庫連接對象,事務(wù)次數(shù),提交次數(shù)以及事務(wù)深度)放入到當(dāng)前線程的ThradLocal當(dāng)中,后面的操作都是基于這個(gè)事務(wù)基礎(chǔ)的,這樣才能保證事務(wù)的原子性。在commit的時(shí)候會(huì)判斷當(dāng)前事務(wù)層次深度,如果為頂層,并且提交次數(shù)+1等于事務(wù)次數(shù)(說明事務(wù)是安全完整的執(zhí)行了),才真正提交到數(shù)據(jù)庫。如果不完整,則在endTrnasaction的時(shí)候會(huì)回滾整個(gè)事務(wù)。
雖然這樣能夠?qū)崿F(xiàn)事務(wù)操作,但是無法實(shí)現(xiàn)跨數(shù)據(jù)庫操作,要實(shí)現(xiàn)跨數(shù)據(jù)庫的事務(wù)估計(jì)只能用JTA了。