JDBC-数据库连接池

目录
一、前言
二、原理
三、连接池种类
四、c3p0数据库连接池
1. 连接步骤
2. 连接方式一(不推荐)
3. 连接方式二(推荐)
五、德鲁伊数据库连接池
1. 连接步骤
2. 连接方式
六、Apche_DbUtils工具类
1. 实现思想
2. 底层模拟实现
3. Apche_DbUtils的使用
   3.1 基本介绍
   3.2 使用示例
七、总结


 如果没有JDBC或者SQL基础的小伙伴,可以查看我下边的有关文章:👇👇👇

JDBC(超详细)JDBC(超详细)
MysqlMysql

一、前言

 在开始本节之前,我们讨论一个问题:为什么需要数据库连接池

🔰传统JDBC连接数据库的弊端

  1. 传统的JDBC数据库连接使用DriverManager来获取,每次向数据库建立连接的时候都要将Connection加载到内存中,再验证IP地址用户名密码(0.05s ~1s时间)。每次需要数据库连接的时候,就向数据库要求一个,频繁的进行数据库连接操作将占用很多的系统资源,容易造成服务器崩溃。
  2. 每一次数据库连接,使用完后都得断开,如果程序出现异常而未能关闭,将导致数据库内存泄漏,最终将导致重启数据库
  3. 传统获取连接的方式,不能控制创建的连接数量,如连接过多,也可能导致内存泄漏,MySQL崩溃

 而解决传统开发中的数据库连接问题,可以采用数据库连接池(connection pool)。

二、原理

📌数据库连接池基本介绍

  1. 预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去
  2. 数据库连接池负责分配管理释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。
  3. 当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。

📌原理图

在这里插入图片描述

三、连接池的种类

 JDBC的数据库连接池使用javax.sq1.DataSource来表示,DataSource只是一个接口,该接口通常由第三方提供实现。

💡最常使用的数据库连接池有:

  1. c3p0数据库连接池:速度相对较稳定性不错
  2. DBCP数据库连接池:速度相对c3p0较快,但不稳定。
  3. Proxool数据库连接池:有监控连接池状态的功能,稳定性较C3P0差一点
  4. BoneCP 数据库连接池:速度快。
  5. Druid(德鲁伊)是阿里提供的数据库连接池,集DBCP、c3p0、Proxool优点于一身的数据库连接池。

 我们下边讲解Druid(德鲁伊)c3p0这两种数据库连接池。🔻

四、c3p0 数据库连接池

📥c3p0jar包下载地址

1. 连接步骤

在这里插入图片描述

2. 连接方式一(不推荐)

 我们方式一读取相关的属性值和JDBC(超详细)这篇文章中的方式一样:都是用properties配置文件。

📌properties配置文件

driverClassNam=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/goodsSystem?serverTimezone=UTC
username=用户名
password=密码

📌示例代码

// 方式一
@Test
public void c3p0_01() throws Exception{
	// 1.创建数据源对象
    ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
    // 2.通过配置文件获取相关连接的信息
    Properties properties = new Properties();
    properties.load(new FileReader("src\\mysql.properties"));
    // 3.读取相关的属性值
    String url = properties.getProperty("url");
    String user = properties.getProperty("user");
    String password = properties.getProperty("password");
    String driver = properties.getProperty("driver");
    // 4.给数据源设置相关的属性
    comboPooledDataSource.setDriverClass(driver);
    comboPooledDataSource.setJdbcUrl(url);
    comboPooledDataSource.setUser(user);
    comboPooledDataSource.setPassword(password);
    comboPooledDataSource.setInitialPoolSize(10);
    comboPooledDataSource.setMaxPoolSize(50);
    long start = System.currentTimeMillis();
    for (int i = 0; i < 5000; i++) {
        Connection connection = comboPooledDataSource.getConnection();
        connection.close();
    }
    long end = System.currentTimeMillis();
    System.out.println("用时" + (end - start));
}

👆这里还测试了获取5000次连接所需要的时间。

3. 连接方式二(推荐)

方式二我们使用XML文件来配置相关信息。

📌XML配置文件

<c3p0-config>
    <!--数据源名称代表连接池-->
    <named-config name="jl">
    <!--驱动类-->
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
    <!--url-->
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/bd1?serverTimezone=UTC</property>
	<!--用户名-->
            <property name="user">用户名</property>
	<!--密码-->
        <property name="password">密码</property>
	<!--每次增长的连接数-->
        <property name="acquireIncrement">5</property>
	<!--初始的连接数-->
        <property name="initialPoolSize">10</property>
	<!--最小的连接数-->
        <property name="minPoolSize">5</property>
	<!--最大连接数-->
        <property name="maxPoolSize">50</property>
	<!--可连接的最多的命令对象数-->
        <property name="maxStatementsPerConnection">2</property>
    </named-config>
</c3p0-config>

⭕这里将XML配置文件放到src根目录下。

📌示例代码

@Test
public void c3p0_02() throws SQLException {
    // 此参数和XML配置文件中的<named-config name="jl">对应。
    ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("jl");
    long start = System.currentTimeMillis();
    for (int i = 0; i < 5000; i++) {
        Connection connection = comboPooledDataSource.getConnection();
        connection.close();
    }
    long end = System.currentTimeMillis();
    System.out.println("用时:" + (end - start));
}

👆这里也测试了获取5000次连接所需要的时间。

五、德鲁伊数据库连接池

📥德鲁伊jar包下载地址

1. 连接步骤

在这里插入图片描述

2. 连接方式

📌properties配置文件

#key=value
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/goodsSystem?serverTimezone=UTC
username=用户名
password=密码
#initial connection Size
initialSize=10
#min idle connecton size
minIdle=5
#max active connection size
maxActive=50
#max wait time (5000 mil seconds)
maxWait=5000

📌示例代码

@Test
public void druid_01() throws Exception {
    Properties properties = new Properties(); //创建Properties对象
    properties.load(new FileReader("src\\druid.properties")); 
    DataSource dataSource = DruidDataSourceFactory.createDataSource(properties); //创建数据库连接池
    long start = System.currentTimeMillis();
    for (int i = 0; i < 10000000; i++) {
        Connection connection = dataSource.getConnection(); // 获得连接池内的连接
        connection.close();
    }
    long end = System.currentTimeMillis();
    System.out.println("用时:" + (end - start));
}

👆这里测试了获取10000000次连接所需要的时间。

💡说明:

  1. 使用德鲁伊连接池,关闭的时候使用的close()Druid的方法,是将当前连接的引用断开,而不是真正的断开连接
  2. 德鲁伊得到的connection运行类型是:class com.alibaba.druid.pool.DruidPooledConnection
  3. 传统方式得到的connection运行类型是:class com.mysql.cj.jdbc.ConnectionImpl
  4. c3p0得到的connection运行类型是:class com.mchange.v2.c3p0.impl.NewProxyConnection

六、Apche_DbUtils工具类

1. 实现思想

在这里插入图片描述

 在Java中万事万物皆对象,所以每条数据就是一个对象,而数组就是和该对象对应类型的数组。
 使用此种方法是因为:

  1. 结果集是和connection关联的,如果关闭连接就不能使用结果集
  2. 结果集不利于数据的管理。

2. 底层模拟实现

这里只是演示一下它这个工具类的基本原理并不是真正官方的实现 ‼👇

@Test
public void apche_01() throws Exception{
    Properties properties = new Properties();
    properties.load(new FileReader("src\\druid.properties"));
    DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
    Connection connection = dataSource.getConnection();
    String sql = "select * from `actor`";
    PreparedStatement preparedStatement = connection.prepareStatement(sql);
    ResultSet resultSet = preparedStatement.executeQuery();
    ArrayList<Actor> list = new ArrayList<>(); // Actor类是sql查询的每条数据对应的类
    while(resultSet.next()){
        String name = resultSet.getString(1);
        Integer salary = resultSet.getInt(2);
        list.add(new Actor(name,salary));
    }
    connection.close();
    resultSet.close();
    preparedStatement.close();
    for (Actor l:list) {
        System.out.println(l);
    }
}

3. Apche_DbUtils的使用

📥Apche_DbUtils下载地址

3.1 基本介绍

commons-dbutils是 Apache组织提供的一个开源JDBC工具类库,它是对JDBC的封装,使用dbutils能极大简化JDBC编码的工作量。
 在工具类中有一个类和一个接口比较重要:

  1. QueryRunner类:该类封装了SQL的执行,是线程安全的。可以实现增、删、改、查、批处理使用QueryRunner类实现查询。
  2. ResultSetHandler接口:该接口用于处理java.sql.ResultSet将数据按要求转换为另一种形式

📑ResultSetHandler的实现子类

实现子类作用
ArrayHandler把结果集中的第一行数据转成对象数组
BeanListHandler把结果集中的每一行数据都转成一个数组,再存放到List中
BeanHandler将结果集中的第一行数据封装到一个对应的JavaBean实例中。
ColumnListHandler将结果集中某一列的数据存放到List中。
KeyedHandler(name)将结果集中的每行数据都封装到Map里,再把这些map再存到一个map里,其key为指定的key.
MapListHandler将结果集中的每一行数据都封装到一个Map里,然后再存放到List
MapHandler将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。

3.2 使用示例

📌查询演示

@Test
public void  apche_02(){
    Connection connection = null;
    List<Actor> list = null;
    try {
        connection = JdbcUtilByDruid.getConnection();
        String sql = "select * from actor where name = ?";
        QueryRunner queryRunner = new QueryRunner();
        list = queryRunner.query(connection, sql, new BeanListHandler<>(Actor.class), "小王"); //query方法就是执行sql语句,得到resultset将其封装到ArrayList集合中
        // new BeanListHandler<>(Actor.class):整个参数是将resultset转换为Actor对象,最后将其封装到ArrayList中(其中使用到了反射机制)
        // 最后的“小王”,是给sql语句中的?赋值。
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }finally {
        JdbcUtilByDruid.close(null,null,connection);
    }
    for (Actor actor: list) {
        System.out.println(actor);
    }
}

📌DML演示

@Test
public void dml() throws Exception{
    Connection connection = JdbcUtilByDruid.getConnection();
    QueryRunner queryRunner = new QueryRunner();
    //        String sql = "insert into actor values(?,?)"; // 增
    //        String sql = "delete from actor where salary = ?"; // 删
    String sql = "update actor set name = ? where name = ?"; // 改
    int update = queryRunner.update(connection,sql,"小赵","小王");
    System.out.println(update>0 ? "成功" : "失败");
    JdbcUtilByDruid.close(null,null,connection);
}
}

💡关于上述代码的几点说明:

  1. JdbcUtilByDruid.getConnection();是通过我们自己封装的一个工具类来获取的。
  2. 上边只演示了查询数据是单个对象的。其余种类的转换,读者可以自行验证。
  3. 为什么没有手动关闭ResultSetStatement。这是因为:在调用query()的时候它自己关闭了ResultsetPreparedstatement

🔻query()底层源码

private <T> T query(Connection conn, boolean closeConn, String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
    if (conn == null) {
        throw new SQLException("Null connection");
    } else if (sql == null) {
        if (closeConn) {
            this.close(conn);
        }

        throw new SQLException("Null SQL statement");
    } else if (rsh == null) {
        if (closeConn) {
            this.close(conn);
        }

        throw new SQLException("Null ResultSetHandler");
    } else {
        PreparedStatement stmt = null; 
        ResultSet rs = null;
        Object result = null;
// 底层分别创建了PreparedStatement。ResultSet(结果集),result(结果数字)
        try {
            stmt = this.prepareStatement(conn, sql);
            this.fillStatement(stmt, params); //给sql语句设置参数(给?,赋值)
            rs = this.wrap(stmt.executeQuery()); // 执行sql语句
            result = rsh.handle(rs); // 对结果集进行处理
        } catch (SQLException var33) {
            this.rethrow(var33, sql, params);
        } finally {
            try {
                this.close(rs); // 关闭结果集
            } finally {
                this.close(stmt); // 关闭PreparedStatement
                if (closeConn) {
                    this.close(conn); 
                }

            }
        }
        return result; //最后返回结果
    }
}

七、总结

以上就是这节的全部内容。主要讲了德鲁伊c3p0这两种数据库连接池。其余的数据库连接池以后用到,也会总结进来。😜

最后希望大家多多 关注+点赞+收藏^_^,你们的鼓励是我不断前进的动力!!!
感谢感谢~~~🙏🙏🙏

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

艺术留白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值