目录

数据库连接池

传统获取Connection问题分析

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

数据库连接池基本介绍

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

数据库连接池种类

  1. JDBC数据库连接池使用javax.sql.DataSource 来表示,DataSource只是一个接口,该接口通常由第三方提供实现
  2. C3P0数据库连接池,速度相对较慢,稳定性不错 (hibernate,spring)
  3. DBCP 数据库连接池,速度相对c3p0较快,但不稳定
  4. Proxool数据库连接池,有监控连接池状态的功能,稳定性较c3p0差一点
  5. BoneCP 数据库连接池,速度快
  6. Druid(德鲁伊) 是阿里提供的数据库连接池,集DBCP,C3P0,Proxool优点于一身的数据库连接池

C3P0连接池使用

先要下载好相关jar包,或maven依赖

C3P0连接数据库方式1:

相关参数在程序中指定(user ,password,url,driver)

//方式1:相关参数在程序中指定(user ,password,url,driver)
    public static void testC3p0_01() throws IOException, PropertyVetoException, SQLException {
        //获取数据源对象
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
        //读取配置信息
        Properties properties = new Properties();
        properties.load(new FileReader("src/mysql.properties"));
        String driver = properties.getProperty("driver");
        String url = properties.getProperty("url");
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        //设置配置信息
        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) +" ms");//耗时: 702 ms
    }

输出:

耗时: 702 ms

C3P0连接数据库方式2:

通过配置文件获取: 配置文件: c3p0-config.xml 文件名一定要是这个,要不然程序找不到,可以放在项目的src目录下,通过配置文件可以设置很多值,可以从官方文档中获取

<?xml version="1.0" encoding="UTF-8"?>

<c3p0-config>
    <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc</property>
        <property name="user">root</property>
        <property name="password"></property>
    </default-config>
    <!-- This app is massive! -->
    <named-config name="ThinkNet">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false&amp;rewriteBatchedStatements=true</property>
        <property name="user">root</property>
        <property name="password"></property>
        <!-- 初始连接数-->
        <property name="initialPoolSize">10</property>
        <!-- 最小连接数-->
        <property name="minPoolSize">5</property>
        <!-- 最大连接数-->
        <property name="maxPoolSize">50</property>
    </named-config>
</c3p0-config>

测试程序:

 //方式2,直接读取配置文件连接
    public static void testC3p0_02() throws SQLException {


        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("ThinkNet");

        long start = System.currentTimeMillis();
        for (int i=0;i<500000;i++){
            Connection connection = comboPooledDataSource.getConnection();
            connection.close();
        }
        long end = System.currentTimeMillis();
        System.out.println("耗时: "+ (end-start) +" ms");//耗时: 1366 ms

    }

输出:

耗时: 1366 ms

德鲁伊(Druid)连接池使用

先要下载好相关jar包,或maven依赖

Druid连接数据库

首先要有配置文件*.properties,文件名可以自定义,路径自定义,加载到程序中就可以

driverClassName = com.mysql.jdbc.Driver
#url,?后面是批处理和解决idea警告的语句,可以不写
url =jdbc:mysql://localhost:3306/jdbc?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=utf-8&useSSL=false
#用户名
username = root
#数据库的密码
password =
initialSize = 10
#闲置最小连接数
minIdle = 5
#最大连接数
maxActive = 50
#最大等待时间
maxWait = 5000
#timeBetweenEvictionRunsMillis = 60

测试代码:

 public static void druidTest() throws Exception {
        //加载配置文件
        Properties properties = new Properties();
        properties.load(new FileReader("src\\druid.properties"));
        //创建一个指定参数的数据库连接池(druid连接池)
        DataSource dataSource= DruidDataSourceFactory.createDataSource(properties);
        //获得连接
        long start = System.currentTimeMillis();
        for (int i=0;i<500000;i++){
            Connection connection = dataSource.getConnection();
            connection.close();
        }
        long end = System.currentTimeMillis();
        System.out.println("耗时: "+ (end-start) +" ms");//耗时: 548 ms

    }

输出:

耗时: 548 ms

从上面的测试果可以看出:用配置文件连接的C3P0连接池,和Druid连接池在500000次连接数据库中,Druid性能要优于C3P0

将Druid连接池封装成工具类

工具类:

public class JdbcUtilsByDruid {
    static DataSource ds;
    static {
        Properties properties = new Properties();
        try {
            properties.load(new FileReader("src\\druid.properties"));
            ds = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
           //转成运行时异常
            throw new RuntimeException(e);
        }
    }
    //获取连接
    public static Connection getConnection(){
        try {
            return ds.getConnection();
        } catch (SQLException e) {
            //转成运行时异常
            throw new RuntimeException(e);
        }
    }
    //关闭连接(不是真正意义上的关闭)
    public static void close(Connection connection, Statement statement, ResultSet resultSet){
        try {
            if(connection!=null){
                connection.close();
            }
            if(statement!=null){
                statement.close();
            }
            if (resultSet!=null){
                resultSet.close();
            }
        } catch (SQLException e) {
            //转成运行时异常
            throw new RuntimeException(e);
        }

    }
}

测试案例

 public static void main(String[] args) {
        String sql="select * from actor";
        Connection connection=null;
        PreparedStatement preparedStatement=null;
        ResultSet resultSet=null;
        try {
            connection=JdbcUtilsByDruid.getConnection();
            //看看它的运行类型是什么
            System.out.println(connection.getClass()); //class com.alibaba.druid.pool.DruidPooledConnection
            preparedStatement=connection.prepareStatement(sql);
            resultSet = preparedStatement.executeQuery();
            while (resultSet.next()){
                int id = resultSet.getInt("id");
                String name = resultSet.getString("name");
                String sex = resultSet.getString("sex");
                Date borndate= resultSet.getDate("borndate");
                String phone = resultSet.getString("phone");
                System.out.println("\t"+id+"\t"+name+"\t"+sex+"\t"+borndate+"\t"+phone+"\t");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtilsByDruid.close(connection,preparedStatement,resultSet);
        }

    }

输出:

class com.alibaba.druid.pool.DruidPooledConnection
	2	jack	男	2002-02-25	119	
	3	king	男	2021-10-10	120	
	4	tom	男	2020-02-10	110	

commons-dbutils

ResultSet弊端

  1. 当Connection连接关闭后,ResultSet将不可用,这样就不能对ResultSet里的数据达到复用;
  2. ResultSet 不利于对数据的管理
  3. 返回数据也不方便

Apache的commons-dbutils基本介绍:

  1. commons-dbutils是Apache组织提供的一个开源JDBC工具类库,它是对JDBC的封装,使用dbutils能极大简化jdbc编码的工作量

dbutils中的类

  1. QueryRunner类: 该类封装了sql的执行,是线程安全的.可以实现增,删,改,查,批处理
  2. ResultSetHandler接口:该接口用于处理java.sal,ResultSet,将数据转换为另一种形式
  3. 常用类
说明
ArrayHandler 把结果集中的第一行数据转成对象数组
ArrayListHandler 把结果集中的没一行数据都转成一个数组,放到List中
BeanHandler 将结果集中的第一行数据封装到一个对应的javaBean实例中
BeanListHandler 将结果集中的每一行数据都封装到一个对应的javaBean实例中,存放在List里
ColumnListHandler 将结果集中某一列的数据存放到List中
KeyedHandler(name) 将结果集中的每行数据都封装到Map里,再把这些map再存到一个map里,其key为指定的key
MapHandler 将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值
MapListHandler 将结果集中的每一行数据都封装到一个Map里,然后再存放到List

使用案例

返回表中所有行

 //返回表中所有行,返回List
    public  static void query() throws Exception {
        String sql="select * from actor where id>?";
        //获得配置文件
        Properties properties = new Properties();
        properties.load(new FileReader("src//druid.properties"));
        //获取数据库连接池
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        //得到连接
        Connection connection = dataSource.getConnection();
        //参数1:将得到的连接扔进去;
        //参数2:sql语句扔进去;
        //参数3:创建一个BeanListHeader对象,将根据表抽象出来的对象的类对象丢进去,底层利用反射机制获取表的各个字段信息
        //参数4:sql语句中的?的值,参数4是可变参数,还可以往下追加
        /**
         * 调用new QueryRunner().query(connection, sql, new BeanListHandler<>(Actor.class), 2)方法后,该方法调用以下方法,closeConn参数为假
         * 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; //给出PreparedStatement对象
         *             ResultSet rs = null; //给出ResultSet对象
         *             Object result = null; //最终这个变量将返回一个ArrayList对象
         *
         *             try {
         *                 stmt = this.prepareStatement(conn, sql);//将sql语句放入PreparedStatement对象中
         *                 this.fillStatement(stmt, params); //将sql语句中的问号填满
         *                 rs = this.wrap(stmt.executeQuery()); //得到结果集处理后给rs
         *                 result = rsh.handle(rs); //底层利用反射机制获取Actor信息,并返回装有Actor对象的ArrayList集合
         *             } catch (SQLException var33) {
         *                 this.rethrow(var33, sql, params);
         *             } finally {
         *                 try {
         *                     this.close(rs); 关闭ResultSet对象rs
         *                 } finally {
         *                     this.close(stmt); //关闭PreparedStatement对象stmt
         *                     if (closeConn) { //参数为假,不走下面的语句,所以在要外面关闭
         *                         this.close(conn);
         *                     }
         *
         *                 }
         *             }
         *
         *             return result; //返回ArrayList集合
         *         }
         *     }
         */
        List<Actor> list = new QueryRunner().query(connection, sql, new BeanListHandler<>(Actor.class), 0);
        for (Actor actor : list) {
            System.out.println(actor);
        }
        //关闭连接(ResultSet和PreparedStatement底层已经关闭)
        connection.close();

    }

输出:

	2	jack	男	2002-02-25 00:00:00.0	119	
	3	king	男	2021-10-10 00:00:00.0	120	
	4	tom	男	2020-02-10 00:00:00.0	110	

返回表中的单行

 //获取一行数据,返回Actor对象
    public static void beanHandlerTest() throws Exception {
        String sql="select * from actor where id=?";
        //获得配置文件
        Properties properties = new Properties();
        properties.load(new FileReader("src//druid.properties"));
        //获取数据库连接池
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        //得到连接
        Connection connection = dataSource.getConnection();
        Actor query = new QueryRunner().query(connection, sql, new BeanHandler<>(Actor.class), 2);
        System.out.println(query);
        //关闭连接
        JdbcUtilsByDruid.close(connection,null,null);
    }

输出:

2	jack	男	2002-02-25 00:00:00.0	119	

获取表中的单行单列数据

    //获取单行单列数据,返回Object对象
    public static void ScalarHandlerTest() throws Exception {
        String sql="select `name` from actor where id=?";
        //获得配置文件
        Properties properties = new Properties();
        properties.load(new FileReader("src//druid.properties"));
        //获取数据库连接池
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        //得到连接
        Connection connection = dataSource.getConnection();
        Object o = new QueryRunner().query(connection, sql, new ScalarHandler<>(), 2);
        System.out.println(o);
        JdbcUtilsByDruid.close(connection,null,null);
    }

输出:

jack

对表进行dml操作

 public static void updateTest() throws Exception {
        //增
        //String sql="insert into actor values(?,?,?,?,?)";
        //删
       // String sql="delete  from actor where id=? ";
        //改
        String sql="update  actor set name =? where id=? ";

        //获得配置文件
        Properties properties = new Properties();
        properties.load(new FileReader("src//druid.properties"));
        //获取数据库连接池
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        //得到连接
        Connection connection = dataSource.getConnection();
        //执行对表的增删改操作(dml语句)
        int rows = new QueryRunner().update(connection, sql,"king" ,3 );
        System.out.println(rows>0?"执行成功":"没有受影响的行数");
        JdbcUtilsByDruid.close(connection,null,null);
    }

输出:表已改变,不给出了.