JDBC连接------事务处理(ThreadLocal的使用)
目录
1、绪论
2、关于ThreadLocal
3、代码实现
3-1、utils层
3-2、service层
3-3、DAO层
3-4、测试方法
总结
之前的的JDBC连接都是写的单线程连接。如果考虑并发问题,用户A进行转账操作,把自己的前转A到另一个用户账户上,自己的账户获取一次连接减少前,另一个账户加钱又获取一次同样的连接。如果对这个连接不加以限制,在这个时候又有另外的用户B进行转账操作,此时极有可能获取的是用户A的连接(如果不限制连接,这种情况是极有可能发生的,而且相当的严重),那么B用户可做的操作就可以影响到A,造成错误的结果!那么我们就需要对我们的连接进行一个限制,即每个用户只能使用自己的连接,不能使用别人的连接。这里每个用户可以视为一个线程。
java中为我们提供了这样一个类ThreaLocal,线程本地变量,也有叫线程本地存储的。在JDBC连接中,可以用这个类来存储连接给线程去用,每个线程从ThreadLocal中获取连接,每个线程都只能使用他自己获取的这个连接,不能使用其他线程的连接
ThreadLocal底层相当于一个map数组,key用来存储当前线程,value用来存储当前线程下共享的数据。它里面有一些方法需要说明一下。
1、get() 获取ThreadLocal中当前线程共享变量的值。
2、set(T value) 设置ThreadLocal中当前线程共享变量的值。
3、remove()移除ThreadLocal中当前线程共享变量的值
ThreadLocal深度解析参考下面文章,此处不作赘述
https://www.jianshu.com/p/98b68c97df9b
C3P0工具类在前面文章中有
package mytest.fenceng2.utils;
import java.sql.Connection;
import java.sql.SQLException;
/**
* @Author: ${user}
*/
public class CollectionManager {
private static ThreadLocal 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();
}
}
}
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();
}
}
}
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;
}
}
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源码解析
能力尚浅,有待进步,如有不足,不吝赐教!