数据库连接池

传统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);  // 关闭连接
        }
    }
}
相关推荐
pokemon..36 分钟前
MySQL主从复制与读写分离
数据库·mysql
码农鑫哥的日常41 分钟前
MySQL高可用配置及故障切换
数据库·mysql
longlongqin1 小时前
redis的 stream数据类型实现 消息队列?
数据库·redis·缓存
wrx繁星点点1 小时前
多个线程同时写入一个共享变量,会发生什么问题?如何解决?
java·开发语言·数据库
鲨鱼辣椒ii1 小时前
sql中索引查看是否生效
数据库·sql
leidata2 小时前
MySQL系列—10.Innodb行格式
数据库·mysql
阿维的博客日记2 小时前
聚簇索引和二级索引
数据库·聚簇索引·二级索引
璇嘟嘟3 小时前
springboot-创建连接池
数据库
计算机程序设计开发3 小时前
小说阅读网站登录注册搜索小说查看评论前后台管理计算机毕业设计/springboot/javaWEB/J2EE/MYSQL数据库/vue前后分离小程序
数据库·vue.js·spring boot·java-ee·课程设计·计算机毕业设计·数据库管理系统
赵利伟@3 小时前
springboot对数据库进行备份+对一个文件夹内的文件按时间排序,只保留最近的8个文件
数据库