ThreadLocal

  在"事务传递Connection"参数案例中,我们必须传递Connection对象,才可以完成整个事务操作.如果不传递参数,是否可以完成?在JDK中给我们提供了一个工具类ThreadLocal.此类可以在一个线程中共享数据

java.lang.ThreadLocal:该类提供了线程局部(thread-local)变量,用于在当前线程中共享数据.

ThreadLocal

java.lang.ThreadLocal该类提供了线程局部(thread-local)变量,用于在当前线程中共享数据,ThreadLocal工具类底层就是一个相当于一个Map,key存放的当前线程,value存放需要共享的数据.

package com.qingmu;

/**
 * @Auther:qingmu
 * @Description:脚踏实地,只为出人头地
 * @Date:Created in 16:08 2019/5/25
 */
public class ThreadLocalTest {
    public static void main(String[] args) {
        ThreadLocal<String> stringThreadLocal = new ThreadLocal<>();
        stringThreadLocal.set("青木");
        System.out.println(stringThreadLocal.get());

        new Thread(()->
                System.out.println(stringThreadLocal.get())).start();
    }
}

结果为:

ThreadLocal(在一个线程中共享数据)_sql

总结:向ThreadLocal中添加的数据只能在当前线程中使用.

小案例的应用:

ThreadLocal(在一个线程中共享数据)_自定义异常_02

ThreadLocal(在一个线程中共享数据)_数据_03

ThreadLocal(在一个线程中共享数据)_sql_04

工具类

public class C3P0Utils {
    //创建一个C3P0的连接池对象(使用c3p0-config.xml中default-config标签中对应的参数)
    public static DataSource ds = new ComboPooledDataSource();
    //给当前线程绑定 连接
    private static ThreadLocal<Connection> local = new ThreadLocal<Connection>();
    /**
     * 获得一个连接
     */
    public static Connection getConnection(){
        try {
    //#1从当前线程中, 获得已经绑定的连接
            service层
            Connection conn = local.get();
            if(conn == null){
        //#2 第一次获得,绑定内容 – 从连接池获得
                conn = ds.getConnection();
          //#3 将连接存 ThreadLocal
                local.set(conn);
            }
            return conn; //获得连接
        } catch (Exception e) {
      //将编译时异常 转换 运行时 , 以后开发中运行时异常使用比较多的。
            throw new RuntimeException(e);
/*
类与类之间 进行数据交换时,可以使用return返回值。也可以使用自定义异常返回值,调用者try{}
catch(e){ e.getMessage() 获得需要的数据}
此处可以编写自定义异常。
*/
//throw new MyConnectionException(e);
        }
    }
}

service层

public class AccountService {
    /**
     * 事务管理方式:向下传递Connection。有侵入性。使用DBUtils
     * 业务层事务管理转账的方法
     * @param from
     * @param to
     * @param money
     */
    public void transfer(String from, String to, double money) {
        //调用dao层
        AccountDao accountDao = new AccountDao();
        //DBUtils进行事务处理的原理,是在Service层获得连接,以保证事务处理过程中的Connection对象为同一个Connection。
        //因为必须保证连接为同一个连接,所以在业务层获得连接,再将连接传递到持久层,代码具有侵入性。
        //DBUtils使用的方法
        Connection conn = null;
        try {
            //获得连接
            conn = C3P0Utils.getConnection();
            //设置事务不自动提交
            conn.setAutoCommit(false);
            //调用持久层
            accountDao.outMoney(from,money);
            //如果有异常
//int a = 1 / 0 ;
            accountDao.inMoney(to,money);
            //提交事务,并安静的关闭连接
            DbUtils.commitAndCloseQuietly(conn);
        } catch (SQLException e) {
        //有异常出现时,回滚事务,并安静的关闭连接
            DbUtils.rollbackAndCloseQuietly(conn);
            e.printStackTrace();
        }
    }
}

dao层

public class AccountDao {
    /**
     * 付款方法
     * @param from 付款人
     * @param money 金额
     */
    public void outMoney(String from, double money) {
        QueryRunner qr = new QueryRunner();
        try {
            Connection conn = C3P0Utils.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 = C3P0Utils.getConnection();
            String sql = "update account set money = money + ? where name = ?";
            qr.update(conn, sql, money,to);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}