JDBC連接------事務處理(ThreadLocal的使用)

JDBC連接------事務處理(ThreadLocal的使用)

目錄

1、緒論

2、關於ThreadLocal

3、代碼實現

3-1、utils層

3-2、service層

3-3、DAO層

3-4、測試方法

總結



1、緒論

之前的的JDBC連接都是寫的單線程連接。如果考慮併發問題,用戶A進行轉賬操作,把自己的前轉A到另一個用戶賬戶上,自己的賬戶獲取一次連接減少前,另一個賬戶加錢又獲取一次同樣的連接。如果對這個連接不加以限制,在這個時候又有另外的用戶B進行轉賬操作,此時極有可能獲取的是用戶A的連接(如果不限制連接,這種情況是極有可能發生的,而且相當的嚴重),那麼B用戶可做的操作就可以影響到A,造成錯誤的結果!那麼我們就需要對我們的連接進行一個限制,即每個用戶只能使用自己的連接,不能使用別人的連接。這裏每個用戶可以視爲一個線程。

java中爲我們提供了這樣一個類ThreaLocal,線程本地變量,也有叫線程本地存儲的。在JDBC連接中,可以用這個類來存儲連接給線程去用,每個線程從ThreadLocal中獲取連接,每個線程都只能使用他自己獲取的這個連接,不能使用其他線程的連接

2、關於ThreadLocal

ThreadLocal底層相當於一個map數組,key用來存儲當前線程,value用來存儲當前線程下共享的數據。它裏面有一些方法需要說明一下。

1、get() 獲取ThreadLocal中當前線程共享變量的值。

2、set(T value) 設置ThreadLocal中當前線程共享變量的值。

3、remove()移除ThreadLocal中當前線程共享變量的值

ThreadLocal深度解析參考下面文章,此處不作贅述

https://www.jianshu.com/p/98b68c97df9b

3、代碼實現

3-1、utils層

C3P0工具類在前面文章中有

package mytest.fenceng2.utils;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * @Author: ${user}
 */
public class CollectionManager {

    private static ThreadLocal<Connection> tl = new ThreadLocal<>();

    public static Connection getConnection() throws SQLException {
        Connection conn = tl.get();
        //如果容器中沒有連接,就從連接池獲取一個連接存到ThreadLocal中
        if (conn == null) {
            conn = C3P0Utils.getConection();
            tl.set(conn);
        }
        return conn;
    }

    public static void begin() {
        try {
            getConnection().setAutoCommit(false);
        } catch (SQLException e) {


        }
    }

    public static void commit() {
        try {
            getConnection().commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void rollback() {
        try {
            getConnection().rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void close() {

        try {

            getConnection().close();
//爲確保連接使用過後不再被調用,需要刪除此連接
            tl.remove();
            
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }


}

3-2、service層

package mytest.fenceng2.service;

import mytest.fenceng2.dao.TeacherDao;
import mytest.fenceng2.utils.C3P0Utils;
import mytest.fenceng2.utils.CollectionManager;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * @Author: ${user}
 */
public class TeacherService {
    public void transfer(int fclass,int lclass,String fname,String lname) throws SQLException {
        TeacherDao td = new TeacherDao();

        try{


CollectionManager.begin();
            int i = td.firstChange( fclass, fname);
//            System.out.println(1/0);

            int i1 = td.lastChange(lclass, lname);
            if (i==1&&i1==1){
                System.out.println("轉班成功");
                CollectionManager.commit();

            }
        }
        catch (Exception e){
            e.printStackTrace();
            CollectionManager.rollback();


        }finally {
            CollectionManager.close();

        }





    }

}

3-3、DAO層

package mytest.fenceng2.dao;



import mytest.fenceng2.utils.CollectionManager;
import org.apache.commons.dbutils.QueryRunner;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * @Author: ${user}
 */
public class TeacherDao {




    public int firstChange(int tclass,String tname) throws SQLException {
//        conn = C3P0Utils.getConection();
        Connection conn = CollectionManager.getConnection();
        String sql = "update teacher set tclass = ? where tname = ?";
        QueryRunner qr = new QueryRunner();
        int update = qr.update(conn, sql, tclass, tname);
        return update;
    }
    public int lastChange(int tclass,String tname) throws SQLException {
//        conn = C3P0Utils.getConection();
        Connection conn = CollectionManager.getConnection();
        String sql = "update teacher set tclass = ? where tname = ?";
        QueryRunner qr = new QueryRunner();
        int update = qr.update(conn, sql, tclass, tname);
        return update;
    }
}

3-4、測試方法

package mytest.fenceng2.control;

import mytest.fenceng2.service.TeacherService;

import java.sql.SQLException;
import java.util.Scanner;

/**
 * @Author: ${user}
 */
public class TeacherControl {

    public static void main(String[] args) throws SQLException {
        Scanner sc =new Scanner(System.in);
        System.out.println("哪個老師要轉班");
        String fname = sc.next();
        System.out.println("轉到哪個班");
        int fclass = sc.nextInt();
        System.out.println("那這個班老師是誰");
        String lname = sc.next();
        System.out.println("她轉到哪個班");
        int lclass = sc.nextInt();
        TeacherService ts =new TeacherService();
        ts.transfer(fclass,lclass,fname,lname);
    }
}

總結

通過創建CollectionManager工具類,將獲取連接和事務操作的方法寫在一起,增加了代碼的複用性。而ThreadLocal通過底層代碼實現了各個線程只能處理自己的數據,不能處理其他線程的數據,有效的解決了事務處理時的併發問題。

關閉ThreadLocal源碼的解析,在後面的文章中也有給出,此處附上連接,方便跳轉

ThreadLocal源碼解析

能力尚淺,有待進步,如有不足,不吝賜教!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章