JDBC 事務控制
一、簡介:
前面一遍提到了jdbc事務相關的概念。從中了解到事務應具有ACID特性。所以對于javaweb開發來說,某一個service層的方法,應該是一個事務,應該是具有原子性的。特別是當一個service方法中需要調用多次dao層的方法。應該必須要保證,這些多次調用的dao方法必須是要不全部執行成功。要不全部執行失敗。比如說銀行業務的service方法的轉賬方法,需要通過dao調用對源轉賬戶信息進行更新減少指定金額,然后調用dao對目標賬戶信息進行更新增加指定金額。
那么如下保證在跨dao層調用時,必須事務的acid特性呢?
二、解決方法思路:
保證事務的ACID特性,默認情況下對用jdbc對數據庫進行操作事務都是自動的commit狀態的。必須必須要將事務提交改成手動提交。由程序來控制什么一起向數據庫提交。一般來說mysql、sql server與oracle默認的隔離級別是repeatable read級別。可以避免臟讀與不可重復讀。所以需要重點控制的是事務的提交與回滾。
那么一個service方法跨多個dao方法調用,如何保證是一個事務呢?首先要保證是同一連接Connection才有可能保證是同一事務。接著需要關注的是如何在多個dao層中獲取是同一Connection。讓整個應用只有個Connection雖然可以解決同一Connection,但是應用就變成了單線程了。肯定不可以。那么多線程情況下,如何保證同一線程內獲取的Connection都是同一對象呢?ThreadLocal類來幫忙,它可以提供線程局部變量。放入到此ThreadLocal中的對象,在同一線程都保證都到的對象都是一致的。
解決方法:只需要編寫一個TransactionUtils類,此類有一個private static的ThreadLocal tl對象。并且靜態的getConnction方法體中,先判斷tl對象中是否存在Connection對象,存在直接返回tl中的Connection。不存在則先用數據源獲取個Connection對象然后放入到tl中,再返回Connection對象。此外TransactionUtils類還需要提供openTransaction方法、Commit方法、rollback方法,openTransaction、commit與rollback需要的Connection對象都直接找本類的getConnection方法。
接著后面service層,先調用TransactionUtils類的openTransaction方法,再對所有的dao層調用方法都try ... Catch...finally下。Catch中調用TransactionUtils類的rollback方法。finally里中調用 TransactionUtils類的commit方法。
而dao層獲取的Connection都直接找TransactionUtils類的getConnection方法,來確認得到的都是同一Connection對象。
三、示例代碼如下:public class TransactionUtil {
private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
private static DataSource ds;
static {
try {
InputStream in = DbcpUtil.class.getClassLoader()
.getResourceAsStream("dbcpconfig.properties");
Properties props = new Properties();
props.load(in);
ds = BasicDataSourceFactory.createDataSource(props);
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
public static DataSource getDataSource() {
return ds;
}
public static Connection getConnection() {
Connection conn = tl.get(); // 從ThreadLoacl中獲取,如果沒有再從DataSource中獲取
if (conn == null) {
try {
conn = ds.getConnection();
tl.set(conn); // 存到ThreadLoacl中
} catch (SQLException e) {
e.printStackTrace();
}
}
return conn;
}
public static void startTransaction() {
try {
Connection conn = tl.get();
if(conn == null) { //如果ThreadLoacl中沒有,就從DataSource中獲取
conn = ds.getConnection();
tl.set(conn); //存入
}
conn.setAutoCommit(false);
} catch(Exception e) {
e.printStackTrace();
}
}
public static void rollback() { <span style="font-family: 'Courier New'; ">//回滾事務,在service層try下dao層,在catch處調用rollbakc方法</span>
try {
Connection conn = tl.get();
if(conn != null)
conn.rollback();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void commit() { <span style="font-family: 'Courier New'; ">//在finally里調用提交commint方法</span>
try {
Connection conn = tl.get();
if(conn != null)
conn.commit();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void release() {
try {
Connection conn = tl.get();
if(conn != null) {
conn.close();
tl.remove();
}
} catch(Exception e) {
e.printStackTrace();
}
}
}
來自:http://blog.csdn.net/chenshufei2/article/details/8087662