22【数据库连接池】_22


文章目录

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;
    }
}

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);
    }
  • 15
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值