学习数据库连接池

148 阅读10分钟

一.数据库连接池概念

什么是连接池? 数据库连接池(Database Connection Pool,简称DBCP)是一种数据库连接资源的缓存或池化技术,用于提高数据库操作的效率和性能。

实际开发中“获得连接”或“释放资源”是非常消耗系统资源的两个过程,为了解决此类性能问题,通常情况我们 采用连接池技术,来共享连接Connection。这样我们就不需要每次都创建连接、释放连接了,这些操作都交 给了连接池.

连接池的好处:

用池来管理Connection,这样可以重复使用Connection。 当使用完Connection后,调用Connection的 close()方法也不会真的关闭Connection,而是把Connection“归还”给池

JDBC与数据库连接池的区别:

普通JDBC image.png 数据库连接池方式

image.png 如何使用数据库连接池: Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口。 这样应用程序可以方便的切换不同厂商的连接池! 常见的连接池有 DBCP连接池, C3P0连接池, Druid连接池

1.1常见配置项

image.png 最大空闲时间:

最大空闲连接数是指连接池中允许保持空闲状态的最大连接数。当连接池中的空闲连接数超过这个值时,连接池可能会关闭一些多余的连接,以释放数据库资源。这个参数的设置需要根据数据库服务器的性能、内存资源以及应用程序的并发需求来综合考虑。

最小空闲连接:

最小空闲连接数是指连接池中始终保持打开状态的最小连接数。这个设置确保即使在低负载时,连接池也维持一定数量的活跃连接,以便快速响应请求,减少新建立连接的开销。

二.DBCP连接池

1.2 编写DBCP连接池

package com.shujuchilianjieku.utils;

import org.apache.commons.dbcp.BasicDataSource;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class DBCPUtils {
    //1.定义常量 保存数据库连接的相关信息
    public static final String DRIVERNAME = "com.mysql.jdbc.Driver";
    public static final String URL = "jdbc:mysql://localhost:3306/db5?characterEncoding=UTF-8";
    public static final String USERNAME = "root";
    public static final String PASSWORD = "123123";
    //2.创建连接池对象 (有DBCP提供的实现类)
    public static BasicDataSource dataSource = new BasicDataSource();
    //3.使用静态代码块进行配置
    static{
        dataSource.setDriverClassName(DRIVERNAME);
        dataSource.setUrl(URL);
        dataSource.setUsername(USERNAME);
        dataSource.setPassword(PASSWORD);}
    //4.获取连接的方法
    public static Connection getConnection() throws SQLException {

        //从连接池中获取连接
        Connection connection = dataSource.getConnection();
        return connection;
    }

    //5.释放资源方法
    public static void close(Connection con, Statement statement){

        if(con != null && statement != null){
            try {
                statement.close();
                //归还连接
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }

    public static void close(Connection con, Statement statement, ResultSet resultSet){

        if(con != null && statement != null && resultSet != null) {
            try {
                resultSet.close();
                statement.close();
                //归还连接
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
  }

1.3 测试DBCP连接池

package com.shujuchilianjieku.utils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;


public class TestDBCP {
    /*
     * 测试DBCP连接池
     * */
    public static void main(String[] args) throws SQLException {

        //1.从DBCP连接池中拿到连接
        Connection con = DBCPUtils.getConnection();
        //2.获取Statement对象
       Statement statement = con.createStatement();
        //3.查询所有员工的姓名
        String sql = "select ename from employee";
        ResultSet resultSet = statement.executeQuery(sql);
        //4.处理结果集
        while(resultSet.next()){
            String ename = resultSet.getString("ename");
            System.out.println("员工姓名: " + ename);
        }
        //5.释放资源
        DBCPUtils.close(con,statement,resultSet);
    }
}

三.C3P0连接池

C3P0是一个开源的JDBC连接池,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate、 Spring等

1.2 编写C3P0工具类

package com.shujuchilianjieku.utils;


import com.mchange.v2.c3p0.ComboPooledDataSource;

import com.mysql.jdbc.Statement;

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

public class C3P0Utils {
    //1.创建连接池对象 C3P0对DataSource接口的实现类
       //使用的配置是 配置文件中的默认配置
//public static ComboPooledDataSource dataSource = new ComboPooledDataSource();
    //使用指定的配置
    public static ComboPooledDataSource dataSource = new ComboPooledDataSource("mysql");
    //获取连接的方法
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }

    //释放资源
    public static void close(Connection con, Statement statement){

        if(con != null && statement != null){
            try {
                statement.close();
                //归还连接
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }

    public static void close(Connection con, Statement statement, ResultSet resultSet){

        if(con != null && statement != null && resultSet != null){
            try {
                resultSet.close();
                statement.close();
                //归还连接
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }
}

1.3 测试C3P0工具类

package com.shujuchilianjieku.utils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;

public class TestC3P0 {

    //需求 查询姓名为李白的 记录
    public static void main(String[] args) throws SQLException {

        //1.获取连接
        Connection con = C3P0Utils.getConnection();

        //2.获取预处理对象
        String sql = "select * from employee where ename = ?";
        PreparedStatement ps = con.prepareStatement(sql);

        //3.设置占位符的值
        ps.setString(1,"李白");
        ResultSet resultSet = ps.executeQuery();
        //4.处理结果集
        while(resultSet.next()){
            int eid = resultSet.getInt("eid");
            String ename = resultSet.getString("ename");
            int age = resultSet.getInt("age");
            String sex = resultSet.getString("sex");
            double salary = resultSet.getDouble("salary");
            Date date = resultSet.getDate("empdate");
            System.out.println(eid +" " + ename + " " + age +" " + sex +" " + salary +" "
                    +date);
        }

                       //5.释放资源
               C3P0Utils.close(con,ps,resultSet);

    }


}

四.Druid连接池

1.4编写Druid工具类

通过工厂来来获取 DruidDataSourceFactory类的createDataSource方法

createDataSource(Properties p) 方法参数可以是一个属性集对象

编写Druid工具类代码:

package com.shujuchilianjieku.utils;

import com.alibaba.druid.pool.DruidDataSourceFactory;

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

public class DruidUtils {

    //1.定义成员变量
    public static DataSource dataSource;

    //2.静态代码块
    static{
        try {
            //3.创建属性集对象
            Properties p = new Properties();

            //4.加载配置文件 Druid 连接池不能够主动加载配置文件 ,需要指定文件
            InputStream inputStream = DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties");

            //5. 使用Properties对象的 load方法 从字节流中读取配置信息
            p.load(inputStream);

            //6. 通过工厂类获取连接池对象
            dataSource = DruidDataSourceFactory.createDataSource(p);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //获取连接的方法
    public static Connection getConnection(){

        try {

            return dataSource.getConnection();

        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }
    }

    //获取Druid连接池对象的方法
    public static DataSource getDataSource(){
        return dataSource;
    }


    //释放资源
    public static void close(Connection con, Statement statement){

        if(con != null && statement != null){
            try {
                statement.close();
                //归还连接
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }

    public static void close(Connection con, Statement statement, ResultSet resultSet){

        if(con != null && statement != null && resultSet != null){
            try {
                resultSet.close();
                statement.close();
                //归还连接
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }

}

1.5 测试Druid工具类

package com.shujuchilianjieku.utils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class TestDruid {
    // 查询 薪资在3000到5000之间员工的姓名
    public static void main(String[] args) throws SQLException {
        //1.获取连接
        Connection con = DruidUtils.getConnection();
        //2.获取Statement对象
        Statement statement = con.createStatement();
        //3.执行查询
        ResultSet resultSet = statement.executeUpdate("select ename from employee where salary between 3000 and 5000");
        //4.处理结果集对象
        while(resultSet.next()){
            resultSet.getString("ename");
            System.out.println(ename);


        }
        //5.释放资源
        DruidUtils.close(con,statement,resultSet);
    }
}

五.DBUtils工具类介绍

使用JDBC我们发现冗余的代码太多了,为了简化开发 我们选择使用 DbUtils Commons DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程 序的开发,同时也不会影响程序的性能。

DBUtils核心功能

  1. QueryRunner 中提供对sql语句操作的API.
  2. ResultSetHandler接口,用于定义select操作后,怎样封装结果集.
  3. DbUtils类,他就是一个工具类,定义了关闭资源与事务处理相关方法

1.6 JavaBean组件

JavaBean 就是一个类, 开发中通常用于封装数据,有以下特点

  1. 需要实现 序列化接口, Serializable (暂时可以省略)
  2. 提供私有字段: private 类型 变量名;
  3. 提供 getter 和 setter
  4. 提供 空参构造

创建Employee类和数据库的employee表对应

public class Employee implements Serializable{

   //成员变量的名称 与 表中的列要一样
    private int eid;
    private String ename;
    private  int age;
    private  String sex;
    private  double salary;
    private Date empdate;

1.7 QueryRunner核心类以及创建

image.png 创建:

image.png

QueryRunner核心类实现对数据库增删改操作:

package com.shujuchilianjieku.utils.testDBUtils;
import com.shujuchilianjieku.utils.DruidUtils;
import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.dbutils.QueryRunner;
import java.sql.Connection;
import java.sql.SQLException;
/**
 * 使用QueryRunner对象 完成增删改
 *              update(Connection con,String sql,Object... param)方法
 */
public class DBUtilsDemo02 {
    public static void main(String[] args) {
        try {
            new DBUtilsDemo02().testDelete();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public void testInsert() throws SQLException {
        // 1. 创建QueryRunner 手动模式创建
        QueryRunner qr = new QueryRunner();
        // 2. 编写占位符方式 SQL
        String sql = "insert into employee values(?,?,?,?,?,?)";
        //3.设置占位符的参数
        Object[] param = {null,"张百万",20,"女",10000,"1990-12-16"};
        //4.执行update方法

        Connection con = DruidUtils.getConnection();
        int i = qr.update(con, sql, param);
        //5.关闭连接(释放资源)
       // con.close();
        DbUtils.closeQuietly(con);

    }
    //修改操作(修改姓名为张百万员工的工资为15000)
    public void testUpdate() throws SQLException {

        //1.创建核心类(自动模式) 需要传递 数据库连接池对象
        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
        //2.编写SQL
        String sql = "update employee set salary = ? where ename = ?";
        //3.设置占位符的参数
        Object[] param = {15000,"张百万"};
        //4.执行修改操作 自动模式不需要传入connection对象
        qr.update(sql,param);
    }
    //删除操作(删除id为1的记录)
    public void  testDelete() throws SQLException {
            QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
            String sql = "delete from employee where eid = ?";
            //如果只有一个参数不需要创建数组
        qr.update(sql,1);
    }
}

1.8 QueryRunner查询操作

ResultSerHandler接口介绍:

ResultSetHandler可以对查询出来的ResultSet结果集进行处理,达到一些业务上的需求。

本例展示的是使用ResultSetHandler接口的几个常见实现类实现数据库的增删改查,可以大大减少代码量,优化 程序。

image.png ArrayHandler类实现:

public void testFinById() throws SQLException {
    //1.创建QueryRunner(自动模式)
    QueryRunner qr =new QueryRunner(DruidUtils.getDataSource());
    //2.编写SQL
    String sql="select * from employee where eid =?";
    //3.执行查询
    Object[] query = qr.query(sql, new ArrayHandler(),5);
    //4.获取数据
    System.out.println(Arrays.toString(query));

}

ArrayListHandler实现:

public void testFindAll() throws SQLException {
    //1.创建QueryRunner
    QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
    //2.编写SQL
    String sql = "select * from employee";
    //3.执行查询
    List<Object[]> query = qr.query(sql, new ArrayListHandler());
    //4.遍历数组获取数据
    for (Object[] objects : query) {
        System.out.println(Arrays.toString(objects));
}

BeanHandler实现

  public void testFindByIdJavaBean() throws SQLException {
      QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
      String sql = "select * from employee where eid = ?";
      Employee employee = qr.query(sql, new BeanHandler<Employee>(Employee.class), 3);
      System.out.println(employee);

  }

BeanListHandler实现:

public void testFindBySalary() throws SQLException {
    QueryRunner qr =new QueryRunner(DruidUtils.getDataSource());
    String sql = "select * from employee where salary > ?";
    List<Employee> list = qr.query(sql, new BeanListHandler<Employee>(Employee.class), 3000);
    for (Employee employee : list) {
        System.out.println(employee);
        
    }

MapHandler实现:

public void testFindByname() throws SQLException {

    QueryRunner qr =new QueryRunner(DruidUtils.getDataSource());
    String sql = "select * from employee where ename = ?";
    Map<String, Object> map = qr.query(sql, new MapHandler(), "张百万");
    Set<Map.Entry<String,Object>> entries=map.entrySet();
    for (Map.Entry<String, Object> entry : entries) {
        //打印结果
        System.out.println(entry.getKey()+"="+entry.getValue());

    }

ScalarHandler实现:

public void testCount() throws SQLException {
    QueryRunner qr =new QueryRunner(DruidUtils.getDataSource());
    String sql = "select sum(salary) from employee";
   Double sum= (Double)qr.query(sql, new ScalarHandler<>());
    System.out.println("员工薪资总额:"+sum);
}

六.数据库批处理

什么是批处理

批处理(batch) 操作数据库 批处理指的是一次操作中执行多条SQL语句,批处理相比于一次一次执行效率会提高很多。 当向数据库中添加大量的数据时,需要用到批处理

用代码实现批处理

image.png

package com.shujuchilianjieku.utils.testbatch;

import com.shujuchilianjieku.utils.DruidUtils;

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

public class Batchinsert {
    //使用批处理 向表中添加10000条数据
    public static void main(String[] args) throws SQLException {
        //1.获取连接
        Connection connection = DruidUtils.getConnection();
        //2.获取预处理对象
        PreparedStatement ps = connection.prepareStatement("insert into testBatch(uname) values(?)");
        //3.执行批量插入操作
        for (int i = 0 ; i<10000 ; i++){
            ps.setString(1,"小强" +1);

            // 将SQL添加到批处理列表
            ps.addBatch();


        }
        //添加时间戳 测试执行效率
        long start = System.currentTimeMillis();

       //4.统一执行批量插入操作
        ps.executeBatch();
        // 结束时间
        long end = System.currentTimeMillis();

        System.out.println("插入10000条数据需要使用:"+(end-start)+"毫秒!");
        //5. 关闭连接
        DruidUtils.close(connection,ps);
    }
}

七.MySql元数据

1.1 元数据基本概念

image.png

元数据查询代码:

SHOW STATUS; 
-- 2 .查看MySql的版本信息
SELECT VERSION();
-- 3.查询表中的详细信息
SHOW COLUMNS FROM employee;
-- 4.显示数据库表的详细索引信息
SHOW INDEX FROM testBatch;
-- 5.列出所有数据库
SHOW DATABASES;
-- 6.显示当前数据库的所有表
SHOW TABLES;
-- 7.获取当前的数据库名
SELECT DATABASE();

2.2 使用JDBC 获取元数据

image.png 获取数据库URL:

package com.shujuchilianjieku.utils.testmetadata;

import com.shujuchilianjieku.utils.DruidUtils;

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

public class TestMetaDate {
    public static void main(String[] args) {
        try {
            new TestMetaDate().testDataBaseMetaData();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    //1.获取数据库相关的元数据信息 使用DatabaseMetaData
    public void testDataBaseMetaData() throws SQLException {
        //1.获取数据库连接对象 connection
        Connection connection = DruidUtils.getConnection();
        //2.获取代表数据库 的元数据对象 DatabaseMetaData
        DatabaseMetaData databaseMetaData = connection.getMetaData();
        //3.获取数据库相关的元数据信息
        String url = databaseMetaData.getURL();
        System.out.println("数据库URL:"+url);
        connection.close();


    }
}

获取当前用户:

String userName = databaseMetaData.getUserName();
System.out.println("当前用户:"+userName);

数据库产品名:

String productName = databaseMetaData.getDatabaseProductName();
System.out.println("数据库产品名:"+productName);

数据的版本号

String version = databaseMetaData.getDriverVersion();
System.out.println("数据库版本:"+version);

2.3 使用JDBC获取结果集元数据信息

使用ResultSetMetaData方法 image.png 代码示例:

public void testResultSetMetaData() throws SQLException {
    //1.获取连接
    Connection con = DruidUtils.getConnection();
    //2.获取预处理对象
    PreparedStatement ps = con.prepareStatement("select * from employee");
    ResultSet resultSet = ps.executeQuery();
    //3.获取结果集元数据对象
    ResultSetMetaData metaData = ps.getMetaData();
   // 获取当前结果集共有多少列
    int count = metaData.getColumnCount();
    System.out.println("当前结果集中共有:"+count+"列");
   // 获取指定列号的列名 参数是整数 从1开始
    String Name = metaData.getColumnName(1);
    System.out.println("1列的列名是:"+Name);
    //获取指定列号列的类型 参数是整数 从1开始
    String TypeName = metaData.getColumnTypeName(1);
    System.out.println("类型:"+TypeName);



    // 释放资源
    DruidUtils.close(con,ps,resultSet);
}

八.总结

image.png