Druid连接池和Apache的DBUtils

背景

jdbc连接数据库存在着大批量用户进行短时间的SQL连接操作的 需求,而普通用户连接后直接断开与数据库的连接,下次连接需要重新建立桥梁,再频繁访问时。这是很消耗性能的一个操作,因此诞生了数据库连接池技术。提前创建 一些连接,避免频繁的创建连接,并且可以管理程序和数据库之间的连接,动态分配桥梁给申请连接的应用程序。

Druid连接池

  • Druid(德鲁伊)连接池是阿里开发的,据说是集DBCP、C3P0、Proxool优点于一身的数据库连接池。

一、如何使用Druid连接池

  1. 引入德鲁伊连接池jar包和jdbc驱动jar包(网上搜索一下,很容易找到下载,注意是.jar文件)
  2. 代码步骤:
    • 第一步:建立一个数据库连接池对象
    • 第二步:设置连接池的参数
    • 第三步:从连接池中获取连接对象来使用
  3. 代码示例
java 复制代码
@Test
    public void test1() throws SQLException {
        DruidDataSource ds = new DruidDataSource();

        //设置基本参数
        ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/atguigu");
        ds.setUsername("root");
        ds.setPassword("123456");

        //设置其他连接参数
        ds.setInitialSize(10);//初始化连接数
        ds.setMaxActive(11);//最大活动连接数
        ds.setMaxWait(1000);//最大等待时间
        ds.setMinIdle(5);//最小连接数

        for (int i = 0; i < 30; i++) {
            DruidPooledConnection conn = ds.getConnection();
            System.out.println("conn = " + conn);
            conn.close();//释放连接到连接池中
        }
    }

二、封装Druid连接池

1.配置文件参数批量导入

Druid连接池提供了一个createDataSource(Properties prop)的方法来统一设置连接池的参数,由于底层是采用硬编码编写的,所以配置文件的变量名称必须与代码一致,否则无法将配置读取到项目中。

具体步骤:

  1. 读取配置文件的参数信息
  2. 使用工厂设计模式批量给参数赋值
  3. 创建线程池ds
  4. 调用线程的getConnection方法来获取一个连接
java 复制代码
public class JdbcTool2 {

    private static DataSource ds;

    static {
        //1.注册驱动
        Properties prop = new Properties();
        try {
            //加载配置文件中的数据到Properties中
            //配置文件要放到src目录下
            prop.load(JdbcTools.class.getClassLoader().getResourceAsStream("jdbc.properties"));
            //批量设置参数的大小
            ds = DruidDataSourceFactory.createDataSource(prop);
            //Class.forName(className);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static Connection getConnection() throws SQLException {
        //获取连接
        return ds.getConnection();
    }
}

2.绑定连接对象

在MVC分层模型中,如果我们在DAO层中使用jdbc技术建立连接修改了账号表的数据,而在业务层中为了模拟转账的需求又再次获取了一个Connection对象来实现该事务。该写法会导致我们重复创建了不同的连接,并且当代码出现异常执行事务回滚时会发现不能恢复到原有的状态。这是由于两层代码中的连接对象并不是同一个。

解决方案如下:

  1. 在jdbc工具类中定义一个ThreadLocal对象
  2. 在静态代码块中将其实例化
  3. 获取连接时使用类似单例模式的写法来将连接与线程绑定在一起
sql 复制代码
public class JdbcTool3 {

    private static DataSource ds;
    private static ThreadLocal<Connection> threadLocal;


    static {
        //1.注册驱动
        Properties prop = new Properties();
        try {
            //加载配置文件中的数据到Properties中
            prop.load(JdbcTools.class.getClassLoader().getResourceAsStream("jdbc.properties"));//配置文件要放到src目录下
            //批量设置参数的大小
            ds = DruidDataSourceFactory.createDataSource(prop);
            //Class.forName(className);

            //3.创建线程
            threadLocal = new ThreadLocal<>();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static Connection getConnection() throws SQLException {
        //获取连接
        Connection conn = threadLocal.get();
        if(conn==null){
            conn = ds.getConnection();
            threadLocal.set(conn);
        }
        return conn;
    }

    public static void closeAll(ResultSet rs, Statement st, Connection conn){
        try {
            if(rs!=null){
                rs.close();
            }
            if(st!=null){
                st.close();
            }
            if(conn!=null){
                conn.close();//将连接还给连接池
                threadLocal.remove();//把连接对象从当前线程移除
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public static DataSource getDs(){
        return ds;
    }
}

示例代码

三、Apche的DBUtils

一个已经封装好了的工具类,可以简化我们在编写DAO层代码的复杂度。主要分两个方面来介绍一下该工具类,分别是增删改和查询操作。

1.update操作

简化之前的代码:

java 复制代码
//添加用户
    public int addUser(User user) throws SQLException {
        Connection conn = JdbcTool3.getConnection();
        PreparedStatement pst = conn.prepareStatement("insert into users(username, password) values(?,?)");
        pst.setObject(1,user.getUsername());
        pst.setObject(2,user.getPassword());
        int i = pst.executeUpdate();
        JdbcTool3.closeAll(null,pst,conn);
        return i;
    }

使用DBUtils后的代码:

java 复制代码
//添加用户
    public int addUser(User user) throws SQLException {
        String sql = "insert into users(username, password) values(?,?)";
        int update = qr.update(sql, user.getUsername(), user.getPassword());
        return update;
    }

2. query操作

简化之前的代码:

java 复制代码
//根据id查询用户
    public User getUserById(int id) throws SQLException {
        Connection conn = JdbcTool3.getConnection();
        PreparedStatement pst = conn.prepareStatement("select id, username, password from users where id = ?");
        pst.setObject(1,id);
        ResultSet rs = pst.executeQuery();
        User user = null;
        if(rs.next()){
            int ID = rs.getInt("id");
            String username = rs.getString("username");
            String password = rs.getString("password");
            System.out.print("ID = " + ID);
            System.out.print(",username = " + username);
            System.out.println(",password = " + password);
            user = new User(ID, username, password);
        }
        JdbcTool3.closeAll(null,pst,conn);
        return user;
    }

使用DBUtils后的代码:

java 复制代码
public User getUserById2(int id) throws SQLException {
        String sql = "select id, username, password from users where id = ?";
        return qr.query(sql,new BeanHandler<>(User.class),id);
    }

其中query方法中的BeanHandler对象底层是通过反射的方式来动态的将对象创建出来,可以选择相应容器来包装,如果是单个字段的话,可以使用相应的Handle对象来处理。

相关推荐
SelectDB5 小时前
基于 Apache Doris 搭建 RAG 系统:从基础 RAG 到知识图谱增强的完整实现与选型指南
apache
SelectDB5 小时前
SelectDB Enterprise 4.0.5:企业级实时分析与 AI 数据底座怎么选?安全合规配置指南
apache
Csvn9 小时前
📊 SQL 入门 Day 12: 窗口函数基础
后端·sql
安_10 小时前
MySQL SQL 调优完整指南
数据库·sql·mysql
隔窗听雨眠12 小时前
80TB电商数据迁移实录:从PostgreSQL分析困境到Apache Doris架构突围
postgresql·架构·apache
我命由我1234513 小时前
SQLite 数据库参数数量超限导致的崩溃问题:SQLiteException: too many SQL variables
java·数据库·sql·java-ee·sqlite·android-studio·android runtime
Lethehong15 小时前
【金仓数据库征文】AI 直连国产数据库——KES MCP Server 自然语言查库与 SQL 调优实战
数据库·人工智能·sql·金仓数据库征文
阿里云大数据AI技术1 天前
EMR Serverless Spark AI Function 的双维降本实践
人工智能·sql·spark
lsh曙光1 天前
Apache服务
apache
jnrjian1 天前
psql 执行多个 sql 文件
数据库·sql