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源码解析

能力尚浅,有待进步,如有不足,不吝赐教!

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