22【数据库连接池】

追求适度,才能走向成功;人在顶峰,迈步就是下坡;身在低谷,抬足既是登高;弦,绷得太紧会断;人,思虑过度会疯;水至清无鱼,人至真无友,山至高无树;适度,不是中庸,而是一种明智的生活态度。

导读:本篇文章讲解 22【数据库连接池】,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文


上一篇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<Connection> 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;
    }
}

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 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
  • 测试代码:
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.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.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', '江西赣州');
  • 示例代码:
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);
    }
}

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

下一篇未完待续…

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


版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/131689.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!