目录
在“ 事务传递 Connection 参数案例 ” 中,我们必须传递Connection对象,才可以完成整个事务操作。如果不传递参数,是否可以完成? 在 JDK 中给我们提供了⼀个⼯具类: ThreadLocal,此类可以在当前线程中共享数据。 java.lang.ThreadLocal 该类提供了线程局部 (thread-local) 变量,⽤于在当前线程中共享数据。
ThreadLocal类的使用
java.lang.ThreadLocal
该类提供了线程局部
(thread-local) 变量,⽤于在当前线程中共享数据。 ThreadLocal
⼯具类底层就是相当于⼀个
Map
,
key
存放的当前线程,
value
存放需要共享的数据。
时间 | 线程A(已开启) | 线程B(已开启) |
T1 | 创建ThreadLocal对象 | |
T2 | 调用set(“wensong”)存入值 | |
T3 | 调用get()获取值 | |
T4 | 调用get()获取值,结果为null |
举例:
public class ThreadLocalDemo {
public static void main(String[] args) {
ThreadLocal<String> mainThread = new ThreadLocal<>();
mainThread.set("wensong");
System.out.println(mainThread.get());//wensong
new Thread(()->{
System.out.println(mainThread.get());//null
}).start();
}
}
结论:向ThreadLocal对象中添加的数据只能在当前线程下使⽤。
案例:使⽤ThreadLocal完成转账
分析
代码实现
工具类
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
/**
* 阿⾥巴巴的连接池 Druid ⼯具类
*/
public class DruidUtils {
/*
1. 加载 druid.properties 配置⽂件
2. 创建 Druid 连接池对象
3. 提供 获得 连接池对象的⽅法
4. 提供 通过ThreadLocal 从连接池中 获取连接对象Connection的 ⽅法
*/
public static DataSource ds = null;
//给定当前线程绑定连接
private static ThreadLocal<Connection> local = new ThreadLocal<>();
static {
try {
//1.加载druid.properties配置文件
InputStream is = DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties");
Properties prop = new Properties();
prop.load(is);
//2。创建Druid连接池
ds = DruidDataSourceFactory.createDataSource(prop);
} catch (Exception e) {
e.printStackTrace();
}
}
/*
3. 提供 获得 连接池对象的⽅法
*/
public static DataSource getDataSource() {
return ds;
}
/*
4. 提供 通过ThreadLocal 从连接池中 获取连接对象Connection的 ⽅法
步骤:
1.从ThreadLocal获得连接
2.如果没有, 从连接池获得连接, 并保存到ThreadLocal中
3.获得连接,返回即可
*/
public static Connection getConnection(){
try{
//1,从当前线程中,获得已经绑定的连接
Connection conn = local.get();
if(conn==null){
//2.第一次使用的话,绑定内容-从连接池获取连接
conn= ds.getConnection();
//3.将连接存在ThreadLocal中
local.set(conn);
}
return conn;//获取连接
} catch (SQLException e) {
//将编译时异常转换运行时,以后开发中运行时异常使用比较多
throw new RuntimeException(e);
}
}
}
⼊⼝程序不变:https://blog.csdn.net/weixin_40959890/article/details/107749459
servise层
import org.apache.commons.dbutils.DbUtils;
import java.sql.Connection;
import java.sql.SQLException;
public class AccountService {
/**
* 业务层事务管理转账的⽅法
*/
public void transfer(String from , String to , double money){
//调用dao层
AccountDao accountdao = new AccountDao();
//DBUtils使用方法
Connection conn = null;
try{
//获取连接
conn = DruidUtils.getConnection();
//设置事务不自动提交
conn.setAutoCommit(false);
//调用持久层
accountdao.outMoney(from,money);
//如果有异常
//int n = 1/0;
accountdao.inMoney(to,money);
//提交事务,并安静的关闭连接
DbUtils.commitAndCloseQuietly(conn);
} catch (SQLException e) {
//出现异常时,回滚事务,并安静关闭连接
DbUtils.rollbackAndCloseQuietly(conn);
e.printStackTrace();
}
}
}
dao
层
import org.apache.commons.dbutils.QueryRunner;
import java.sql.Connection;
import java.sql.SQLException;
public class AccountDao {
/**
* 付款⽅法
* @param from 付款⼈
* @param money ⾦额
*/
public void outMoney(String from, String to, Double money){
QueryRunner qr = new QueryRunner();
try{
Connection conn = DruidUtils.getConnection();
String sql = "update account set money= money -? where name =?";
qr.update(conn,sql,money,from);
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 收款⽅法
* @param to 收款⼈
* @param money ⾦额
*/
public void inMoney(String to,double money){
QueryRunner qr = new QueryRunner();
try {
Connection conn = DruidUtils.getConnection();
String sql= "update account set money = money+? where name=?";
qr.update(conn,sql,money,to);
} catch (SQLException e) {
e.printStackTrace();
}
}
}