数据库连接池

传统JDBC连接存在的问题,每次获取连接都需要去验证driverClass,url,username,password

而应用与数据库DBMS之间的通信是进程间的通信而底层又使用的是Socket,相对耗时

从而引入连接池的概念

连接池负责从DBMS提前获取连接,例如20个conn,一直放在连接池中

用户需要用了,去连接池中获取连接,连接用完还给连接池,但是连接池并没有真正去关闭该连接

而是等待其他线程的再次重用,这样,提高效率,当然很多连接池都配置了连接默认刷新时间,一般是30分钟,主要是网络问题去刷新下连接(就是真正意义上的重新获取连接,当连接还是使用,一般等连接空闲,再刷新)

SpringBoot+Mybatis默认用的是HikariCP 暂时查到的是HikariCP内当最大连接数用完,又没有空闲连接可以使用的时候,并没有提供接受队列,而是进入阻塞,等待有空闲连接可以使用

连接池就是一个技术方面的解决方案

连接池就是一个技术方面的解决方案

连接池就是一个技术方面的解决方案

使用数据库连接池,需要实现 javax.sql.DataSource

为什么使用数据库连接池:简单讲就是为了优化提高效率,减少每次去获取连接的耗时

传统JDBC实现类是:JDBC4Connection

C3P0从连接池获取的连接是:NewProxyConnection

Druid从连接池获取的连接是:DruidStatementConnection

HikariCP从连接池获取的连接时:HikariProxyConnection

使用连接池之后conn.closed方法并非正真意义上的关闭连接,而是将连接交还给数据库连接池

public class ConQuestion {

    /**
     * 传统通过DriverManager方式获取Connection,每次向数据库建立链接的时候
     * 要验证url,用户名,密码,这样效率比较低
     * 而且每次都是获取一个连接
     * 传统获取连接的方式,不能控制创建的丽娜姐数量
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        

        long start = System.currentTimeMillis();
        System.out.println("开始");
        for(int i=0;i<500;i++){
            //传统方式通过DriverManager获取Connection
            Connection connection = JDBCUtils.getConnection();
            //通过SQL执行对象操作数据库.....
            JDBCUtils.closed(null,null,connection);
        }
        System.out.println("结束");
        long end = System.currentTimeMillis();
        System.out.println("耗时:"+(end-start));//这里连的阿里云RDS 50次 耗时:4s  一般连接本地的话 500次大概8秒 用的mysql8.0
    }
}

package com.example;

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;

/**
 * JDBC工具类
 * @author hrui
 * @date 2024/8/29 16:45
 */
public class JDBCUtils {
    private static String url;
    private static String username;
    private static String password;
    private static Properties properties=new Properties();

    static{
        try {
            properties.load(new FileInputStream("src\\jdbc.properties"));
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");
        }catch (IOException e){
            e.printStackTrace();
            //throw new RuntimeException(e);
        }
    }


    public static Connection getConnection() throws SQLException {
        //如果这样写,虽然不需要定义url,username,password,但是每次都要从properties获取url,username,password,效率不高 所以还是定义url,username,password 静态块里赋值
        //return DriverManager.getConnection(properties.getProperty("url"), properties.getProperty("username"), properties.getProperty("password"));
        return DriverManager.getConnection(url, username, password);
    }


    public static void closed(ResultSet rs, Statement stmt, Connection conn){
        try {
            if(rs!=null){
                rs.close();
            }
            if(stmt!=null){
                stmt.close();
            }
            if(conn!=null){
                conn.close();
            }
        }catch (SQLException e){
            e.printStackTrace();
        }
    }
}

src下的jdbc.properties

复制代码
url=jdbc:mysql://localhost:3306/jdbc?rewriteBatchedStatements=true
username=root
password=123456
1.C3P0连接池的使用

依赖

复制代码
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.5.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.mchange/mchange-commons-java -->
<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>mchange-commons-java</artifactId>
    <version>0.2.20</version>
</dependency>

注意:如果新版C3P0只导入C3P0会报错 因此导入mchange-commons-java

C3P0第一个示例demo

 /**
     * 测试C3P0连接池demo1
     */
    @Test
    public void testC3P0_demo1() throws IOException, PropertyVetoException, SQLException {
        //该类最终实现java.sql.DataSource接口
        //创建C3P0连接池
        ComboPooledDataSource dataSource=new ComboPooledDataSource();
        Properties properties=new Properties();
        properties.load(new FileInputStream("src\\jdbc.properties"));
        String url = properties.getProperty("url");
        String username = properties.getProperty("username");
        String password = properties.getProperty("password");

        //连接池会去连接DBMS 告知连接池驱动类型,url,username,password
        dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
        dataSource.setJdbcUrl(url);
        dataSource.setUser(username);
        dataSource.setPassword(password);

        //设置初始化连接数
        dataSource.setInitialPoolSize(5);
        //设置最大连接数
        dataSource.setMaxPoolSize(10);

        //从连接池中获取一个连接
        for(int i=0;i<12;i++){
            Connection conn = dataSource.getConnection();//从连接池获取连接
            System.out.println(conn);
            System.out.println("获取第"+i+"个连接");
            if(i==9){
                conn.close();//该conn是个代理对象,还回数据库连接池  并没有真正关闭
            }
        }
    }

第二种配置c3p0连接池的方式:用配置文件

在src下创建c3p0-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <!-- 配置文件 -->
    <!-- 名称代表连接池 -->
    <named-config name="myc3p0">
        <!-- 驱动包 -->
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
        <!-- 数据库的地址 -->
        <property name="jdbcUrl">jdbc:mysql:///jdbc?serverTimezone=Asia/Shanghai</property>
        <!-- 用户名 -->
        <property name="user">root</property>
        <!-- 密码 -->
        <property name="password">123456</property>
        <!-- 每次增长的连接数 -->
        <property name="acquireIncrement">5</property>
        <!-- 初始化连接对象的数量 -->
        <property name="initialPoolSize">5</property>
        <!-- 最小连接对象的数量 -->
        <property name="minPoolSize">5</property>
        <!-- 最大连接对象的数量 -->
        <property name="maxPoolSize">10</property>

        <!-- 可以缓存的sql语句模板数量,在整个连接池缓存 -->
        <property name="maxStatements">50</property>
        <!-- 每个连接可以缓存的sql语句模板数量 -->
        <property name="maxStatementsPerConnection">20</property>
        <!-- 超时时间 -->
        <property name="checkoutTimeout">3000</property>
    </named-config>
</c3p0-config>

示例代码

 /**
     * 测试C3P0连接池demo2
     * 使用配置文件模板
     * src下配置c3p0-config.xml
     */
    @Test
    public void testC3P0_demo2() throws IOException, SQLException {
        //创建C3P0连接池
        ComboPooledDataSource dataSource=new ComboPooledDataSource("myc3p0");
        Connection connection = dataSource.getConnection();
        System.out.println("从连接池获取连接:"+connection);
        System.out.println(connection.getAutoCommit());
    }
2.Druid(德鲁伊连接池)

<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->

<dependency>

<groupId>com.alibaba</groupId>

<artifactId>druid</artifactId>

<version>1.2.23</version>

</dependency>

直接点击下载

在src下创建druid.properties

复制代码
# 键=值格式的配置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc?rewriteBatchedStatements=true
username=root
password=root

# 初始连接数
initialSize=10

# 最小空闲连接数 最小连接数
minIdle=5

# 最大活跃连接数 也是最大连接数
maxActive=20

# 最大等待时间(5000毫秒)等待的是连接池里的连接
maxWait=5000
package com.example;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;
import org.junit.Test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

/**
 *
 * @author hrui
 * @date 2024/8/30 15:43
 */
public class Druid_ {

    /**
     * 测试Druid连接池
     */
    @Test
    public void testDruid() throws IOException, SQLException {
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\druid.properties"));

        //创建数据源
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(properties.getProperty("driverClassName"));
        druidDataSource.setUrl(properties.getProperty("url"));
        druidDataSource.setUsername(properties.getProperty("username"));
        druidDataSource.setPassword(properties.getProperty("password"));
        druidDataSource.setInitialSize(Integer.parseInt(properties.getProperty("initialSize")));
        druidDataSource.setMaxActive(Integer.parseInt(properties.getProperty("maxActive")));
        druidDataSource.setMaxWait(Integer.parseInt(properties.getProperty("maxWait")));
        Connection connection = druidDataSource.getConnection();
        System.out.println(connection);
        connection.close();//交还给连接池


        long start = System.currentTimeMillis();

        //复用连接池中的连接
        for(int i=0;i<5000;i++){
            Connection conn = druidDataSource.getConnection();//从连接池获取连接
            //System.out.println(conn);
            conn.close();//交还给连接池
        }

        System.out.println("5000次耗时:"+(System.currentTimeMillis()-start));

    }
}

因连接已经在连接池中,因此获取连接的耗时问题基本解决了

Druid连接池工具类
package com.example;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/**
 * 基于Druid数据库连接池的工具类
 * @author hrui
 * @date 2024/8/30 16:18
 */
public class JDBCUtilsByDruid {

    private static DataSource dataSource;

    static {
        Properties properties = new Properties();
        try {
            Properties prop = new Properties();
            prop.load(new FileInputStream("src\\druid.properties"));
            //给数据源(连接池赋值,并创建连接池)
            dataSource = DruidDataSourceFactory.createDataSource(prop);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }


    //还回连接池
    public static void close(ResultSet rs, Statement stmt, Connection conn) {
        if (rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (stmt != null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

获取连接demo

3.HikariCP(SpringBoot+Mybatis默认使用的数据库连接池)

<!-- https://mvnrepository.com/artifact/com.zaxxer/HikariCP -->

<dependency>

<groupId>com.zaxxer</groupId>

<artifactId>HikariCP</artifactId>

<version>5.1.0</version>

</dependency>

//HikariCP内部日志需要

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-api</artifactId>

<version>1.7.36</version>

</dependency>

可以模仿Druid写个工具类

package com.example;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;


import java.sql.Connection;
import java.sql.SQLException;

/**
 * @author hrui
 * @date 2024/8/30 16:56
 */

public class HikariCP_ {
    public static void main(String[] args) throws SQLException {
        // 1. 配置 HikariCP
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://localhost:3306/jdbc");
        config.setUsername("root");
        config.setPassword("123456");
        config.setDriverClassName("com.mysql.cj.jdbc.Driver");

        // 2. 配置连接池属性
        // 设置连接池中允许的最大连接数(10个连接),当所有连接都被占用时,新的连接请求将会被阻塞并进入等待状态,直到有连接被释放
        config.setMaximumPoolSize(10);
        // 设置最小空闲连接数(5个连接),连接池在空闲时最少会保持这些连接数不被关闭
        config.setMinimumIdle(5);
        // 设置获取连接的最大等待时间(30秒),如果在此时间内无法获取到连接,将抛出 SQLException
        config.setConnectionTimeout(30000);
        // 设置连接在连接池中空闲的最大时间(600秒),超过此时间的空闲连接将被释放(超过最小连接<最大连接时候的连接)
        config.setIdleTimeout(600000);
        // 默认就是这个值 30分钟 将连接真正关闭 重新获取新的连接 一个连接用久了可能网络问题等待时间会比较长,所以设置这个时间
        config.setMaxLifetime(1800000);

        // 3. 创建数据源
        HikariDataSource dataSource = new HikariDataSource(config);
        long start = System.currentTimeMillis();
        for(int i=0;i<5000;i++){
            Connection conn = dataSource.getConnection();
            conn.close();
        }
        System.out.println("耗时:"+(System.currentTimeMillis()-start)+"毫秒");
    }

}

并没有Druid快

4.Apache Commons DbUtils:轻量级的 Java 库,用于简化 JDBC 操作,注意本身他没有集成连接池,是对JDBC操作的简化

<!-- https://mvnrepository.com/artifact/commons-dbutils/commons-dbutils -->

<dependency>

<groupId>commons-dbutils</groupId>

<artifactId>commons-dbutils</artifactId>

<version>1.8.1</version>

</dependency>

如果要集成连接池,需要引入对应连接池的jar包

那么有人会说:JDBC的操作很容易,为什么还要引入Apache Commons DbUtils?????

这是你写简单JDBC觉得容易的原因,然而随着项目复杂度的增加,JDBC的操作会变得繁琐且容易出错

例如:

1.资源管理,没有正确关闭,导致的资源耗尽问题

2.Apache Commons DbUtils简化了结果集的处理

3.Apache Commons DbUtils减少了样板代码,就是说它本身已经封装了获取连接,关闭资源等等

4.提高了可维护性

什么时候不需要使用Apache Commons DbUtils

1.简单应用,例如就几次JDBC操作

2.更加复杂的操作,如果使用的是Hibernate,JPA这样的ORM框架

3.也不适用于现在常用的SpringBoot+Mybatis

所以这个选择是否使用Apache Commons DbUtils 还是看你自己意愿

什么时候适合使用Apache Commons DbUtils

简单讲:我觉得如果大量使用JDBC代码,而你又对Apache Commons DbUtils非常了解的情况下,那么可以选择使用Apache Commons DbUtils+其他数据库连接池的方案 是否多余 看你咯

那么传统JDBC有哪些不好为什么要用Apache Commons DbUtils

1.传统JDBC的做法是,获取连接,用sql执行对象发送select/insert/update/delete等操作

2.select查询得到结果集之后,必须遍历完结果集才可以关闭连接conn,这样在多线程时候,还是浪费conn的,就是说并不是理想的结果,如果你提前关闭conn,ResultSet没有办法遍历,会报错,有没有办法,获取Result之后,随时可以关闭连接,其实****Apache Commons DbUtils就是这么做的,内部肯定是想ResultSet存起来了

3.Apache Commons DbUtils将ResultSet存起来,并且封装成了ArrayList<javaBean>,那么ResultSet也就没有用了,conn也随时可以关闭

Apache Commons DbUtils对ResultSet做了封装

package com.example;

import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

/**
 * @author hrui
 * @date 2024/8/30 18:56
 */
public class ResultSetTest {

    public static void main(String[] args) {
        PreparedStatement ps = null;
        ResultSet rs = null;
        Connection conn=null;


        try {
            conn = JDBCUtils.getConnection();
            String sql="select * from t_test";
            PreparedStatement p = conn.prepareStatement(sql);
            rs = p.executeQuery();

            //Apache Commons DbUtils帮我们将ResultSet做了一个封装 并返回
            List<Test> list=new ArrayList<>();
            while (rs.next()){
                int id = rs.getInt(1);
                String name=rs.getString(2);
                String pwd=rs.getString(3);
                Date dirthday = rs.getDate(4);
                System.out.println(id+":"+name+":"+pwd+":"+dirthday);
                //编译器将java.sql.Date;和java.util.Date;做了转换
                Test test=new Test(id,name,pwd,dirthday);
                list.add(test);
            }
            System.out.println(list);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            JDBCUtils.closed(rs,ps,conn);
        }



    }
}
5.使用Apache Commons DbUtils+Druid连接池对表进行增删改查

<!-- https://mvnrepository.com/artifact/commons-dbutils/commons-dbutils -->

<dependency>

<groupId>commons-dbutils</groupId>

<artifactId>commons-dbutils</artifactId>

<version>1.8.1</version>

</dependency>

//德鲁伊连接池

<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->

<dependency>

<groupId>com.alibaba</groupId>

<artifactId>druid</artifactId>

<version>1.2.23</version>

</dependency>

可以直接下载jar包

Druid数据库连接池工具类

package com.example;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/**
 * 基于Druid数据库连接池的工具类
 * @author hrui
 * @date 2024/8/30 16:18
 */
public class JDBCUtilsByDruid {

    private static DataSource dataSource;

    static {
        Properties properties = new Properties();
        try {
            Properties prop = new Properties();
            prop.load(new FileInputStream("src\\druid.properties"));
            //给数据源(连接池赋值,并创建连接池)
            dataSource = DruidDataSourceFactory.createDataSource(prop);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }


    //还回连接池
    public static void close(ResultSet rs, Statement stmt, Connection conn) {
        if (rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (stmt != null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

Druid数据库连接池配置文件

druid.properties

复制代码
# 键=值格式的配置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc?rewriteBatchedStatements=true
username=root
password=123456

# 初始连接数
initialSize=10

# 最小空闲连接数
minIdle=5

# 最大活跃连接数 也是最大连接数
maxActive=20

# 最大等待时间(5000毫秒)
maxWait=5000

报这个错暂时两种方式

1.将实体类java.util.Date去掉改成String

2.mysql驱动换成5.1.6不要使用8版本

3.将实体类Date类型改成LocalDateTime(推荐做法) 或者LocalDate

java.sql.Date是java.util.Date的子类

第三种解决方式,咱不改自己代码,改Apache Commons DbUtils代码,重写下

Apache Commons DbUtils 默认情况下使用 PreparedStatement 来执行 SQL 语句

所以有参数都以PreparedStatement ? ? ?的方式

改成LocalDateTime就好了 最好不要一直这样 我当时实体类还是用的Date

1.查询多条数据
package com.example;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author hrui
 * @date 2024/8/30 19:47
 */
public class CommonDBUtils {

    //使用Apache Commons DbUtils工具类+druid连接池完成表的增删改查
    @org.junit.Test
    public void testQueryMany(){
        Connection conn =null;
        try {
            //从druid数据库连接池得到连接
           conn = JDBCUtilsByDruid.getConnection();
            //获取Commons DbUtils数据库操作对象 进行增删改查都用它好了
            QueryRunner qr = new QueryRunner();
            //List<Test> query = qr.query(conn, "select * from t_test", new BeanListHandler<>(Test.class));
            //mysql8报错  1.将实体类Date类型改成String 2.将mysql驱动改成5.X  3.那就高级了
            //List<Test> query = qr.query(conn, "select * from t_test where id=?", new BeanListHandler<>(Test.class),1);//如果有参数
            //解决mysql驱动8无法将LocalDateTime转换为java.util.Date 原因是mysql8的驱动版本问题 ResultSet 的某些方法返回 java.time.LocalDateTime 而不是 java.util.Date
            List<Test> results = qr.query(conn,"select * from t_test where id>=?", new ResultSetHandler<List<Test>>() {
                @Override
                public List<Test> handle(ResultSet rs) throws SQLException {
                    List<Test> list = new ArrayList<>();
                    while (rs.next()) {
                        int id = rs.getInt("id");
                        String name = rs.getString("name");
                        String pwd = rs.getString("pwd");
                        Date birthday = rs.getDate("birthday");  // 获取 java.sql.Date
                        Test test = new Test(id, name, pwd,birthday); //java.sql.Date是java.util.Date子类 隐式转
                        list.add(test);
                    }
                    return list;
                }
            },1);
            for (Test test : results) {
                System.out.println(test);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //在query方法里会自动关闭rs stmt
            JDBCUtilsByDruid.close(null,null,conn);
        }
    }
}
2.返回单个对象

卧槽 拉倒吧 这样每次都重写 后面将实体类Date字段改成LocalDateTime 也是推荐的日期类型

 //查询返回一条
    @org.junit.Test
    public void testQueryOne(){
        Connection conn =null;
        try {
            //从druid数据库连接池得到连接
            conn = JDBCUtilsByDruid.getConnection();
            QueryRunner qr = new QueryRunner();
            //使用BeanHandler处理器, 返回结果为单个对象 例如登录
            //Test query =qr.query(conn, "select * from t_test where id=?", new BeanHandler<>(Test.class),1);
            Test query = qr.query(conn, "select * from t_test where id=?", new ResultSetHandler<Test>() {
                @Override
                public Test handle(ResultSet resultSet) throws SQLException {
                    Test test = null;
                    while (resultSet.next()) {
                        int id = resultSet.getInt("id");
                        String name = resultSet.getString("name");
                        String pwd = resultSet.getString("pwd");
                        Date birthday = resultSet.getDate("birthday");  // 获取 java.sql.Date
                        test = new Test(id, name, pwd,birthday); //java.sql.Date是java.util.Date子类 隐式转
                    }
                    return test;
                }
            }, 1);
            if(query!=null){
                System.out.println(query);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //在query方法里会自动关闭rs stmt
            JDBCUtilsByDruid.close(null,null,conn);
        }
    }

将实体类Date类型转成LocalDateTime

 @org.junit.Test
    public void testQueryOne(){
        Connection conn =null;
        try {
            //从druid数据库连接池得到连接
            conn = JDBCUtilsByDruid.getConnection();
            QueryRunner qr = new QueryRunner();
            //使用BeanHandler处理器, 返回结果为单个对象 例如登录
            Test query =qr.query(conn, "select * from t_test where id=?", new BeanHandler<>(Test.class),1);

            if(query!=null){
                System.out.println(query);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //在query方法里会自动关闭rs stmt
            JDBCUtilsByDruid.close(null,null,conn);
        }
    }
3.查询返回单行单列
//查询返回单行单列
    @org.junit.Test
    public void testScalar(){
        Connection conn =null;
        try {
            //从druid数据库连接池得到连接
            conn = JDBCUtilsByDruid.getConnection();
            QueryRunner qr = new QueryRunner();
            //使用ScalarHandler处理器
            Object query = qr.query(conn, "select name from t_test where id=?", new ScalarHandler<>(), 1);

            if(query!=null){
                System.out.println("name="+query);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //在query方法里会自动关闭rs stmt
            JDBCUtilsByDruid.close(null,null,conn);
        }
    }
4.增删改DML操作
 //DML操作
    @org.junit.Test
    public void testDML(){
        Connection conn =null;
        try {
            //从druid数据库连接池得到连接
            conn = JDBCUtilsByDruid.getConnection();
            QueryRunner qr = new QueryRunner();
            //使用ScalarHandler处理器
            int insert = qr.update(conn, "insert into t_test value(null,?,?,?)", "hrui", "123456",LocalDateTime.now());

            System.out.println("insert="+insert);
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //在query方法里会自动关闭rs stmt
            JDBCUtilsByDruid.close(null,null,conn);
        }
    }
5.演示批量
 Connection conn = null;
        try {
            // 获取数据库连接
            conn = JDBCUtilsByDruid.getConnection();
            QueryRunner qr = new QueryRunner();

            // SQL 语句
            String sql = "INSERT INTO test (name, pwd, birthday) VALUES (?, ?, ?)";

            // 批量参数:每个 Object[] 表示一行数据
            Object[][] params = new Object[][]{
                {"user1", "password1", LocalDateTime.now()},
                {"user2", "password2", LocalDateTime.now()},
                {"user3", "password3", LocalDateTime.now()}
            };

            // 执行批量插入操作
            int[] results = qr.batch(conn, sql, params);

            // 输出结果
            System.out.println("Inserted rows: " + results.length);

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭连接
            JDBCUtilsByDruid.close(null, null, conn);
        }

 Connection conn = null;
        try {
            // 获取数据库连接
            conn = JDBCUtilsByDruid.getConnection();
            QueryRunner qr = new QueryRunner();

            // SQL 语句
            String sql = "INSERT INTO test (name, pwd, birthday) VALUES (?, ?, ?)";

            // 定义批次大小
            int batchSize = 1000;
            Object[][] params = new Object[batchSize][3];
            
            // 循环生成数据并执行批量插入
            for (int i = 0; i < 1000000; i++) {
                params[i % batchSize] = new Object[]{"user" + i, "password" + i, LocalDateTime.now()};
                
                // 当达到批次大小时执行批量插入
                if ((i + 1) % batchSize == 0) {
                    qr.batch(conn, sql, params);
                    System.out.println("Inserted batch " + (i + 1) / batchSize);
                }
            }

            // 处理剩余未满批次的数据
            if (1000000 % batchSize != 0) {
                qr.batch(conn, sql, params);
                System.out.println("Inserted final batch");
            }

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭连接
            JDBCUtilsByDruid.close(null, null, conn);
        }

事实上对于JDBC操作

Apache Commons DbUtils+druid数据库连接操作就已经满足了大部分需求

一个conn不关闭就好了,其实也用不到什么连接池

Apache Commons DbUtils目的是简化JDBC操作

因此没有提供获取连接的方式

QueryRunner有几个好几个构造方法

6.事务操作

还是和原来一样 通过conn去设置事务

@org.junit.Test
public void testDML() {
    Connection conn = null;
    try {
        // 从 Druid 数据库连接池获取连接
        conn = JDBCUtilsByDruid.getConnection();

        // 关闭自动提交,手动管理事务
        conn.setAutoCommit(false);

        QueryRunner qr = new QueryRunner();

        // 第一个 DML 操作
        int insert1 = qr.update(conn, "insert into t_test value(null,?,?,?)", "hrui", "123456", LocalDateTime.now());
        System.out.println("insert1=" + insert1);

        // 第二个 DML 操作
        int insert2 = qr.update(conn, "insert into t_test value(null,?,?,?)", "john", "654321", LocalDateTime.now());
        System.out.println("insert2=" + insert2);

        // 如果所有操作成功,提交事务
        conn.commit();
    } catch (SQLException e) {
        e.printStackTrace();
        try {
            if (conn != null) {
                // 发生异常时回滚事务
                conn.rollback();
                System.out.println("事务已回滚");
            }
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
    } finally {
        // 关闭连接,释放资源
        JDBCUtilsByDruid.close(null, null, conn);
    }
}

6.Apache Commons DbUtils不使用连接池

6.关于BasicDao的概念
package com.example.dao;

import com.example.JDBCUtilsByDruid;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;

import java.sql.Connection;
import java.util.List;

/**
 * 基类: 所有 Dao 的父类
 * @author hrui
 * @date 2024/8/30 22:55
 */
public class BasicDao<T> {

    private QueryRunner qr = new QueryRunner();

    // 通用 DML 操作(无事务)
    public int update(String sql, Object... params) {
        Connection conn = null;
        try {
            conn = JDBCUtilsByDruid.getConnection();
            return qr.update(conn, sql, params);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            JDBCUtilsByDruid.close(null, null, conn);
        }
    }

    // 返回多行(无事务)
    public List<T> query(String sql, Class<T> cls, Object... params) {
        Connection conn = null;
        try {
            conn = JDBCUtilsByDruid.getConnection();
            return qr.query(conn, sql, new BeanListHandler<>(cls), params);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            JDBCUtilsByDruid.close(null, null, conn);
        }
    }

    // 查询单行结果(无事务)
    public T queryOne(String sql, Class<T> cls, Object... params) {
        Connection conn = null;
        try {
            conn = JDBCUtilsByDruid.getConnection();
            return qr.query(conn, sql, new BeanHandler<>(cls), params);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            JDBCUtilsByDruid.close(null, null, conn);
        }
    }

    // 返回单行单列(无事务)
    public Object queryScalar(String sql, Object... params) {
        Connection conn = null;
        try {
            conn = JDBCUtilsByDruid.getConnection();
            return qr.query(conn, sql, new ScalarHandler<>(), params);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            JDBCUtilsByDruid.close(null, null, conn);
        }
    }

    // 通用 DML 操作(带事务)
    public int update(Connection conn, String sql, Object... params) {
        try {
            return qr.update(conn, sql, params);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    // 返回多行(带事务)
    public List<T> query(Connection conn, String sql, Class<T> cls, Object... params) {
        try {
            return qr.query(conn, sql, new BeanListHandler<>(cls), params);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    // 查询单行结果(带事务)
    public T queryOne(Connection conn, String sql, Class<T> cls, Object... params) {
        try {
            return qr.query(conn, sql, new BeanHandler<>(cls), params);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    // 返回单行单列(带事务)
    public Object queryScalar(Connection conn, String sql, Object... params) {
        try {
            return qr.query(conn, sql, new ScalarHandler<>(), params);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}

使用 BasicDao 进行无事务操作的示例

public class UserDao extends BasicDao<User> {
    
    public int addUser(User user) {
        String sql = "INSERT INTO user (name, password) VALUES (?, ?)";
        return update(sql, user.getName(), user.getPassword());
    }

    public User getUserById(int id) {
        String sql = "SELECT * FROM user WHERE id = ?";
        return queryOne(sql, User.class, id);
    }
}

不带事务的操作示例

public class UserService {
    public static void main(String[] args) {
        UserDao userDao = new UserDao();
        
        User newUser = new User();
        newUser.setName("hrui");
        newUser.setPassword("123456");

        int result = userDao.addUser(newUser);
        System.out.println(result > 0 ? "User added successfully" : "Failed to add user");
    }
}

使用 BasicDao 进行带事务操作的示例

public class UserService {
    private UserDao userDao = new UserDao();

    public void performUserOperations() {
        Connection conn = null;
        try {
            conn = JDBCUtilsByDruid.getConnection();
            conn.setAutoCommit(false);  // 开启事务

            // 在同一个事务中执行多个操作
            userDao.update(conn, "UPDATE user SET name = ? WHERE id = ?", "newName", 1);
            userDao.update(conn, "UPDATE user SET password = ? WHERE id = ?", "newPassword", 1);

            conn.commit();  // 提交事务
        } catch (Exception e) {
            if (conn != null) {
                try {
                    conn.rollback();  // 事务回滚
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }
            throw new RuntimeException(e);
        } finally {
            JDBCUtilsByDruid.close(null, null, conn);  // 关闭连接
        }
    }
}
相关推荐
CoderIsArt1 小时前
Redis的三种模式:主从模式,哨兵与集群模式
数据库·redis·缓存
师太,答应老衲吧3 小时前
SQL实战训练之,力扣:2020. 无流量的帐户数(递归)
数据库·sql·leetcode
Channing Lewis4 小时前
salesforce case可以新建一个roll up 字段,统计出这个case下的email数量吗
数据库·salesforce
毕业设计制作和分享5 小时前
ssm《数据库系统原理》课程平台的设计与实现+vue
前端·数据库·vue.js·oracle·mybatis
ketil275 小时前
Redis - String 字符串
数据库·redis·缓存
Hsu_kk6 小时前
MySQL 批量删除海量数据的几种方法
数据库·mysql
编程学无止境6 小时前
第02章 MySQL环境搭建
数据库·mysql
knight-n7 小时前
MYSQL库的操作
数据库·mysql
包饭厅咸鱼7 小时前
QML----复制指定下标的ListModel数据
开发语言·数据库
生命几十年3万天8 小时前
redis时间优化
数据库·redis·缓存