JDBC 事務控制

jopen 12年前發布 | 52K 次閱讀 JDBC Java開發

一、簡介:

前面一遍提到了jdbc事務相關的概念。從中了解到事務應具有ACID特性。所以對于javaweb開發來說,某一個service層的方法,應該是一個事務,應該是具有原子性的。特別是當一個service方法中需要調用多次dao層的方法。應該必須要保證,這些多次調用的dao方法必須是要不全部執行成功。要不全部執行失敗。比如說銀行業務的service方法的轉賬方法,需要通過dao調用對源轉賬戶信息進行更新減少指定金額,然后調用dao對目標賬戶信息進行更新增加指定金額。

那么如下保證在跨dao層調用時,必須事務的acid特性呢?

二、解決方法思路:

保證事務的ACID特性,默認情況下對用jdbc對數據庫進行操作事務都是自動的commit狀態的。必須必須要將事務提交改成手動提交。由程序來控制什么一起向數據庫提交。一般來說mysqlsql serveroracle默認的隔離級別是repeatable read級別。可以避免臟讀與不可重復讀。所以需要重點控制的是事務的提交與回滾。

那么一個service方法跨多個dao方法調用,如何保證是一個事務呢?首先要保證是同一連接Connection才有可能保證是同一事務。接著需要關注的是如何在多個dao層中獲取是同一Connection。讓整個應用只有個Connection雖然可以解決同一Connection,但是應用就變成了單線程了。肯定不可以。那么多線程情況下,如何保證同一線程內獲取的Connection都是同一對象呢?ThreadLocal類來幫忙,它可以提供線程局部變量。放入到此ThreadLocal中的對象,在同一線程都保證都到的對象都是一致的。

解決方法:只需要編寫一個TransactionUtils類,此類有一個private staticThreadLocal tl對象。并且靜態的getConnction方法體中,先判斷tl對象中是否存在Connection對象,存在直接返回tl中的Connection。不存在則先用數據源獲取個Connection對象然后放入到tl中,再返回Connection對象。此外TransactionUtils類還需要提供openTransaction方法、Commit方法、rollback方法,openTransactioncommitrollback需要的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

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!