• 22【数据库连接池】



    上一篇21【JDBC操作数据库元数据】


    下一篇未完待续…

    目录【MySQL零基础系列教程】



    22【数据库连接池】

    1.1 连接池概述

    我们使用JDBC操作数据库时,必须首先先建立一个Connection网络连接,使用完毕之后再将这个连接关闭释放对应的资源,连接对象需要不停的创建,不停的关闭。而创建/关闭一个网络连接是非常消耗资源的一个操作,我们希望创建出来的连接使用完毕后不关闭,而是交给某个容器进行管理,等到下次使用的时候再从容器中拿出来;这个容器就是我们所说的连接池!

    Tips:连接池的思想和我们之前学习过的线程池的概念是一模一样的,只不过线程池里面存储的都是线程,而连接池里面存储的都是Connection连接;

    • 没有使用连接池的示例图:

    在这里插入图片描述

    • 连接对象的使用问题:

      • 1)数据库创建连接通常需要消耗相对较多的资源,创建时间也较长,而每次操作都要重新获取新的连接对象,执行一次操作就把连接关闭,这样数据库连接对象的使用率低。
      • 2)假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出
    • 连接池需要解决两个问题

      • 1)提高创建连接对象的速度
      • 2)提高连接对象的使用率,每个连接对象应该重复使用

    使用连接池的情况:

    在这里插入图片描述

    1.2 连接池的使用

    1.2.1 DataSource接口

    在JDK中提供有一个连接池的顶层接口,但JDK本身并没有具体实现,由第三方厂商来实现;这里也是采用面向对象来进行解耦操作;

    • 数据源接口中的方法:
    DataSource接口中的方法描述
    Connection getConnection()从连接池中得到一个连接对象
    • 常用连接池参数
    常用参数描述
    初始连接数连接池创建的时候,一开始在连接池中有多少个连接对象
    最大连接数连接池中最多可以创建多少个连接对象
    最长等待时间如果连接池中所有的连接对象都在使用,新来的用户等待多久以后抛出异常。单位毫秒
    最长空闲回收时间如果某个连接对象长时间没有用户使用,多久以后进行回收。不是所有的连接池都支持。

    需要注意的是,这些参数并不是JDK中规定的,而是我们认为一个连接池中应该要具备的一些公共参数,而且JDK中并没有对连接池进行实现,连接池具体会有那些参数可以设置这个JDK也并没有进行规范,而是交给其他的第三方厂商来自行决定,我们要根据使用的连接池厂商不同来具体的设置连接池参数;

    1.2.2 自定义连接池

    1)创建数据源MyDataSource类:
    1. 接口:DataSource,包含getConnection()方法,抛出SQLException
    2. 类:MyDataSourceDataSource接口
    3. 成员对象:private static LinkedList pool
      用于存放连接对象,相当于连接池。
    4. 成员变量:initPoolSize初始连接数5,currSize当前连接数0,maxPoolSize最大连接数10
    5. 私有成员方法: Connection
      createConnection(),返回一个连接对象,每创建一个currSize加1.
    6. 构造方法:在构造方法中调用方法创建5个连接对象放在集合中,添加到集合末尾。
    7. 公有方法:Connection getConnection()方法,三种情况选其中一种。
      1. 如果当前连接池中有连接对象(即size大于0),则直接从集合中removeFirst()得到一个连接
      2. 如果当前连接池没有连接对象(即size小于0),并且连接数小于最大连接数,调用createConnection()创建连接返回。
      3. 如果当前连接数已经等于最大连接数,则抛出SQLException异常。
    8. 公有方法:releaseConnection(Connection conn),把连接对象addLast()放回池中。
    2)代码实现
    package com.dfbz.pool;
    
    import javax.sql.DataSource;
    import java.io.PrintWriter;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.sql.SQLFeatureNotSupportedException;
    import java.util.LinkedList;
    import java.util.logging.Logger;
    
    /**
     * @author lscl
     * @version 1.0
     * @intro: 这是第三方厂商实现连接池的类
     */
    public class MyDataSource implements DataSource {
    
        //创建连接池的几个属性
        private int initPoolSize = 5;       //初始连接数
        private int maxPoolSize = 10;       //最大连接数
    
        private int currSize = 0;           //当前已经创建了多少个连接对象,计数的作用
    
        //创建一个集合,模拟连接池
        private LinkedList<Connection> pool = new LinkedList<>();
    
        //在构造方法中创建好初始的连接数
        public MyDataSource() {
            for (int i = 0; i < initPoolSize; i++) {
                //创建一个连接对象,并且加到池中最后一个
                pool.addLast(createConnection());
            }
        }
    
        //写一个方法创建连接对象
        private Connection createConnection()  {
            try {
                //连接数加1
                currSize++;
                return DriverManager.getConnection("jdbc:mysql:///test?useSSL=true","root","admin");
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
    
    
        /**
         * 得到一个连接对象
         * @return
         * @throws SQLException
         */
        @Override
        public Connection getConnection() throws SQLException {
            //如果连接池中有连接对象,直接返回即可
            if (pool.size() > 0) {
                //从连接池中取一个元素,返回
                return pool.removeFirst();
            }
            //如果连接池中没有对象了,但当前连接数小于最大连接数的
            else if (pool.size() == 0 && currSize < maxPoolSize) {
                return createConnection();  //再创建一个新的连接对象
            }
            //如果已经到达最大连接数
            else {
                throw new SQLException("已经到达最大连接数:" + maxPoolSize);
            }
        }
    
        /**
         * 关闭连接的方法
         */
        public void releaseConnection(Connection conn) {
            //将连接对象放回到连接池中
            pool.addLast(conn);
        }
        
        @Override
        public Connection getConnection(String username, String password) throws SQLException {
            return null;
        }
    
        @Override
        public <T> T unwrap(Class<T> iface) throws SQLException {
            return null;
        }
    
        @Override
        public boolean isWrapperFor(Class<?> iface) throws SQLException {
            return false;
        }
    
        @Override
        public PrintWriter getLogWriter() throws SQLException {
            return null;
        }
    
        @Override
        public void setLogWriter(PrintWriter out) throws SQLException {
    
        }
    
        @Override
        public void setLoginTimeout(int seconds) throws SQLException {
    
        }
    
        @Override
        public int getLoginTimeout() throws SQLException {
            return 0;
        }
    
        @Override
        public Logger getParentLogger() throws SQLFeatureNotSupportedException {
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    4)测试类使用数据源类:
    1. 创建MyDataSource对象
    2. 在类中测试看能否得到1连接对象。
    3. 先从连接池中拿10个输出,再拿11个输出,等待2秒出现异常。
    4. 当i==5时,释放其中一个对象,则可以得到11个连接对象,其中有一个连接是重用的。
    • 代码:
    package com.dfbz.demo;
    
    import com.dfbz.pool.MyDataSource;
    
    import java.sql.Connection;
    
    /**
     * @author lscl
     * @version 1.0
     * @intro:
     */
    public class Demo01_MyDataSource {
        public static void main(String[] args) throws Exception {
    
            //创建数据源(默认初始化10个连接)
            MyDataSource ds = new MyDataSource();
    
            //从数据源中得到10个连接对象
            for (int i = 1; i <= 10; i++) {
                Connection conn = ds.getConnection();
                System.out.println("得到第" + i + "个连接" + conn);
                
                if (i == 5) {
                    // 放到连接池中
                    ds.releaseConnection(conn);
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    1.2 Druid连接池

    1.2.1 Druid简介

    Druid是阿里巴巴开发的号称为监控而生的数据库连接池,在功能、性能、扩展性方面,都超过其他数据库连接池。Druid已经在阿里巴巴部署了超过600个应用,经过多年生产环境大规模部署的严苛考验。如:一年一度的双十一活动,每年春运的抢火车票。

    Druid的下载地址:https://github.com/alibaba/druid

    1.2.2 Druid常用的配置参数

    参数说明
    url连接字符串
    username用户名
    password密码
    driverClassName驱动库com.mysql.jdbc.Driver
    initialSize初始连接数
    maxActive最大连接数
    maxWait最长等待时间

    1.2.3 DRUID连接池基本使用

    • API介绍
    DruidDataSourceFactory的方法方法
    public static DataSource createDataSource(Properties properties)通过属性对象得到数据源对象

    在src目录下新建一个Druid配置文件,命名为:druid.properties(文件名不强制)

    • 参数:初始连接数5个,最大连接池10,最长等待时间2秒
    # 连接字符串
    url=jdbc:mysql://localhost:3306/test?useSSL=true
    # 用户名
    username=root
    password=admin
    driverClassName=com.mysql.jdbc.Driver
    #初始连接数
    initialSize=5
    #最大连接数
    maxActive=10
    #最长等待时间
    maxWait=2000
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 测试代码:
    package com.dfbz.demo;
    
    import com.alibaba.druid.pool.DruidDataSourceFactory;
    
    import javax.sql.DataSource;
    import java.io.InputStream;
    import java.sql.Connection;
    import java.util.Properties;
    
    /**
     * @author lscl
     * @version 1.0
     * @intro:
     */
    public class Demo02_DruidDataSource {
        public static void main(String[] args) throws Exception {
            //创建属性对象
            Properties properties = new Properties();
    
            //src就是类路径所在的目录,从类路径下加载属性文件,转成字节输入流
            InputStream in
                    = Demo02_DruidDataSource.class.getClassLoader().getResourceAsStream("druid.properties");
    
            properties.load(in);
    
            // 根据加载到的配置来创建一个数据源
            DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
    
            // 获取11个连接对象
            for (int i = 1; i <= 11; i++) {
                Connection conn = dataSource.getConnection();
                System.out.println("第" + i + "个连接对象:" + conn);
                // 释放一个
                if (i == 5) {
                    conn.close();
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 执行效果:

    在这里插入图片描述

    1.3 连接池工具类

    1.3.1 创建工具类

    • 示例代码:
    package com.dfbz.utils;
    
    import com.alibaba.druid.pool.DruidDataSourceFactory;
    
    import javax.sql.DataSource;
    import java.io.InputStream;
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.util.Properties;
    
    /**
     * @author lscl
     * @version 1.0
     * @intro: 数据源的工具类
     */
    public class DataSourceUtils {
    
        private static DataSource ds;
    
        /**
         * 在静态代码块中创建数据源对象
         */
        static {
            // 创建属性对象
            Properties info = new Properties();
            try (
                    // 得到输入流
                 InputStream in = DataSourceUtils.class.getClassLoader().getResourceAsStream("druid.properties");) {
                // 加载到属性对象中
                info.load(in);
                ds = DruidDataSourceFactory.createDataSource(info);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 得到数据源
         */
        public static DataSource getDataSource() {
            return ds;
        }
    
    
        /**
         * 从连接池中得到连接对象
         */
        public static Connection getConnection() {
            try {
                return ds.getConnection();
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
    
    
        /**
         * 释放资源
         */
        public static void close(Connection conn, Statement stmt, ResultSet rs) {
            //关闭结果集
            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();
                }
            }
        }
    
        /**
         * 关闭连接
         */
        public static void close(Connection conn, Statement stmt) {
            close(conn, stmt, null);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96

    1.3.2 数据源工具类的使用

    • 1)SQL语句:
    DROP TABLE IF EXISTS `t_user`;
    CREATE TABLE `t_user`  (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
      `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
      `birthday` date NULL DEFAULT NULL,
      `address` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;
    
    -- ----------------------------
    -- Records of t_user
    -- ----------------------------
    INSERT INTO `t_user` VALUES (1, 'root', 'admin', '1998-10-10', '江西景德镇');
    INSERT INTO `t_user` VALUES (2, 'admin', '123', '1997-08-21', '福建福州');
    INSERT INTO `t_user` VALUES (3, '张三', 'admin', '2022-06-03', '江西赣州');
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 示例代码:
    package com.dfbz.demo;
    
    import com.dfbz.utils.DataSourceUtils;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.util.Date;
    
    /**
     * @author lscl
     * @version 1.0
     * @intro:
     */
    public class Demo03_DataSourceUtils {
        public static void main(String[] args) throws Exception {
            //得到连接对象
            Connection conn = DataSourceUtils.getConnection();
    
            //得到预编译的语句对象
            PreparedStatement ps = conn.prepareStatement("select * from t_user;");
    
            //得到结果集
            ResultSet rs = ps.executeQuery();
    
            //封装数据
            while (rs.next()) {
    
                int id = rs.getInt("id");
                String name = rs.getString("username");
                String gender = rs.getString("password");
                Date birthday = rs.getDate("birthday");
                String address = rs.getString("address");
    
                System.out.println("id: " + id);
                System.out.println("name: " + name);
                System.out.println("gender: " + gender);
                System.out.println("birthday: " + birthday);
                System.out.println("address: " + address);
                System.out.println("------------------");
            }
            //关闭连接
            DataSourceUtils.close(conn, ps, rs);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    上一篇21【JDBC操作数据库元数据】


    下一篇未完待续…

    目录【MySQL零基础系列教程】


  • 相关阅读:
    uniapp微信小程序_自定义交费逻辑编写
    SP297 AGGRCOW - Aggressive cows
    JavaScript混淆加密
    AJAX基于XML的数据交换、XML和JSON的区别
    Open set
    java锁
    程序员导航站
    基金的利润分配与税收
    Rust开发WebAssembly在Html和Vue中的应用【后篇】
    Excel VLOOKUP 初学者教程:通过示例学习
  • 原文地址:https://blog.csdn.net/Bb15070047748/article/details/126570139