ThreadLocal 基础 + commons-dbutils -教案

ThreadLocal 基础 + commons-dbutils -教案

文章目录

  • ThreadLocal 基础 + commons-dbutils -教案
    • 基本概念
    • 常用方法
    • 案例
    • 使用 ThreadLocal 管理数据库连接,实现事务管理

基本概念

  • ThreadLocal 用于给当前线程保存一个变量。
  • ThreadLocal 变量的定义一般是static的。普通的static变量是所有线程共享的,ThreadLocal 的静态变量只在当前线程中共享,多个线程之间的线程变量是彼此隔离的。
  • 一个 ThreadLocal 变量只能保存一个值,如果需要多个 ThreadLocal 数据,则需要定义多个 ThreadLocal 变量。

常用方法

  • set():赋值
  • get():取值
  • remove():删除值

案例

案例看点:对比普通静态变量和线程静态变量

public class ThreadLocalTest {
    // 全局静态变量,所有线程共享
    public static Integer integer=null;
    // 线程静态变量,单个线程共享,不同线程具有不同的数据副本(值)
    public static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();

    // 定义内部线程类
    public static class Task implements  Runnable{
        @Override
        public void run() {
            // 产生随机数
            Integer i = new Random().nextInt(100);
            // 设置线程变量的值
            threadLocal.set(i);
            // 保存到普通静态变量
            integer = i;

            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 获得线程变量的值
            Integer ii = threadLocal.get();

            System.out.println("线程"+Thread.currentThread().getName()+" 的threadLocal="+ii);
            System.out.println("线程"+Thread.currentThread().getName()+" 的integer="+integer);

        }
    }

    public static void main(String[] args) {
        // 启动三个线程
        for (int i = 0; i < 3; i++) {
            new Thread(new Task()).start();
        }
    }
}

运行结果,可以看出

  • threadLocal 变量的值在三个线程中的值是不同的
  • 静态的integer变量的值在三个线程中是相同的
线程Thread-1 的threadLocal=40
线程Thread-0 的threadLocal=33
线程Thread-1 的integer=40
线程Thread-2 的threadLocal=88
线程Thread-2 的integer=40
线程Thread-0 的integer=40

使用 ThreadLocal 管理数据库连接,实现事务管理

案例看点:整合了动态代理ThreadLocalcommons-dbutils

  • database.properties 连接信息配置
# 驱动类
driver=com.mysql.cj.jdbc.Driver
# 连接字符串
url=jdbc:mysql://localhost:3306/jsp?useUnicode=true&serverTimezone=GMT&characterEncoding=UTF-8&useSSL=false
# 连接字符串:解决 PublicKey.. 异常
#url=jdbc:mysql://localhost:3306/jsp?useUnicode=true&serverTimezone=GMT&characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true
# 用户名
user=root
# 密码
password=1234
  • ConfigManager.java 连接信息读取类
public class ConfigManager {
    private static Properties props = null;

    static {
        InputStream is = null;
        is = ConfigManager.class.getClassLoader().getResourceAsStream(
                "database.properties");
        if (is == null)
            throw new RuntimeException("找不到数据库参数配置文件!");
        props = new Properties();
        try {
            props.load(is);
        } catch (IOException e) {
            throw new RuntimeException("数据库配置参数加载错误!", e);
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static String getProperty(String key) {
        return props.getProperty(key);
    }
}
  • ConnectionManager.java 连接管理类
public class ConnectionManager {
    private static String driver = ConfigManager.getProperty("driver");// 数据库驱动字符串
    private static String url = ConfigManager.getProperty("url");// 连接URL字符串
    private static String user = ConfigManager.getProperty("user"); // 数据库用户名
    private static String password = ConfigManager.getProperty("password"); // 用户密码
    // 使用ThreadLocal线程变量保存并共享Connection连接对象
    static ThreadLocal<Connection> threadLocalConnection = new ThreadLocal<>();

    // 静态块加载驱动
    static {
        try {
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection(){
        // 取得线程变量中的Connection连接对象
        Connection conn = threadLocalConnection.get();
        if(conn == null){
            try {
                conn = DriverManager.getConnection(url,user,password);
                // 保存到线程变量中
                threadLocalConnection.set(conn);
                // 将连接的事务设置为手动提交,自动提交设置为false
                conn.setAutoCommit(false);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return conn;
    }

    /**
     * 提交事务
     */
    public static void commit(){
        Connection conn = threadLocalConnection.get();
        if(conn != null){
            try {
                conn.commit();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        //一定要执行remove()操作,否则会出错。(因为Tomcat服务器底层使用了线程池技术)
        threadLocalConnection.remove();
    }
    /**
     * 提交事务
     */
    public static void rollback(){
        Connection conn = threadLocalConnection.get();
        if(conn != null){
            try {
                conn.rollback();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        //一定要执行remove()操作,否则会出错。(因为Tomcat服务器底层使用了线程池技术)
        threadLocalConnection.remove();
    }
}
  • TransactionProxyFactory.java动态代理类,代理实现业务逻辑层的事务控制
public class TransactionProxyFactory {
    public static <T> T create(Class<T> clazz){
        Enhancer enhancer = new Enhancer();
        // 采用接口内部类的方式实现代理功能
        enhancer.setCallback(new MethodInterceptor() {
            public Object intercept(Object target, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("开始事务。。。。");
                Object result = null;
                try {
                    // 核心业务,调用父类的方法
                    result = methodProxy.invokeSuper(target,objects);
                    System.out.println("提交...");
                    ConnectionManager.commit();

                }catch (Exception e){
                    System.out.println("回滚...");
                    ConnectionManager.rollback();
                    e.printStackTrace();
                }
                return result;
            }
        });
        // 设置代理类的父类
        enhancer.setSuperclass(clazz);
        // 返回创建的动态代理类对象
        return (T) enhancer.create();
    }
}
  • BaseDao.java使用并封装了commons-dbutils的基本操作
/**
 * 执行数据库操作的工具类。
 */
public class BaseDao<T> {

    private QueryRunner queryRunner = new QueryRunner();

    /**
     * 执行增删该操作
     * @param sql
     * @param args
     * @return 返回受影响的行数
     */
    public int update(String sql,Object ... args){
        Connection conn = ConnectionManager.getConnection();
        try {
            return queryRunner.update(conn,sql,args);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 查询返回一个实体类对象
     * @param type
     * @param sql
     * @param args
     * @return
     */
    public T queryForOne(Class<T> type,String sql,Object...args){
        Connection conn = ConnectionManager.getConnection();
        try {
            T query = queryRunner.query(conn, sql, new BeanHandler<T>(type), args);
            return query;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 查询返回一个实体类对象集合
     * @param type
     * @param sql
     * @param args
     * @return
     */
    public List<T> queryForList(Class<T> type, String sql, Object...args){
        Connection conn= ConnectionManager.getConnection();
        try {
            return queryRunner.query(conn, sql, new BeanListHandler<T>(type), args);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 查询返回一个数据,如统计记录的行数
     * @param sql
     * @param args
     * @return
     */
    public Object queryForSingleValue(String sql,Object...args){
        Connection conn = ConnectionManager.getConnection();
        try {
            return queryRunner.query(conn,sql, new ScalarHandler(),args);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}
  • UserDaoImpl.java 基本DAO类
public class UserDaoImpl extends BaseDao<User> implements UserDao {
    @Override
    public int insert(User user) throws SQLException {
        String sql = "insert into users(userName,password,realName) values(?,?,?)";
        return super.update(sql,
                user.getUserName(),
                user.getPassword(),
                user.getRealName());
    }
}
  • UserService.java业务逻辑类
public class UserService {
    UserDao userDao = new UserDaoImpl();

    /**
     * 批量添加,多个User要么全部添加成功,要么全部添加失败
     * @param users
     * @throws SQLException
     */
    public void insert(List<User> users) throws SQLException {
        // 业务类中只关注核心功能,事务控制交给动态代理
        for (User user : users) {
            userDao.insert(user);
        }
    }
}
  • UserServiceTest.java测试类
public class UserServiceTest {

    @Test
    public void insert() throws SQLException {
        User user1 = new User();
        user1.setUserName("雷老板");
        user1.setPassword("1");
        user1.setRealName("是我雷老板");

        User user2 = new User();
        user2.setUserName("雷老板2");
        // 数据太长了,会添加失败
        user2.setRealName("雷老板222雷老板222雷老板222雷老板222雷老板222雷老板222雷老板222雷老板222雷老板222雷老板222雷老板222雷老板222雷老板222");

        List<User> users = new ArrayList<>();
        users.add(user1);
        users.add(user2);

        // 使用动态代理来创建业务逻辑层的类
        UserService userService = TransactionProxyFactory.create(UserService.class);
        userService.insert(users);
    }
}

你可能感兴趣的:(java,进阶,java,开发语言,c4java)