JDBC 数据库连接池详解和示例

学习目标

  1. 了解数据库连接池的原理
  2. 了解C3P0数据库连接池
  3. 了解DBCP数据库连接池
  4. 掌握Druid数据库连接池

数据库连接池

数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用现有的N个(自定义)数据库连接,不用每次都再重新建立一个新的连接,以提高对数据库操作的性能。

常用开源的数据库连接池

  • C3P0:是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate [2] 一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSources 对象。
  • Proxool:是一个Java SQDriver驱动程序,提供了对选择的其它类型的驱动程序的连接池封装。可以非常简单的移植到现存的代码中,完全可配置,快速、成熟、健壮。可以透明地为现存的JDBC驱动程序增加连接池功能。
  • Jakarta DBCP:DBCP是一个依赖Jakartacommons-pool对象池机制的数据库连接池。DBCP可以直接的在应用程序中使用。
    -DBPool:是一个高效、易配置的数据库连接池。它除了支持连接池应有的功能之外,还包括了一个对象池,使用户能够开发一个满足自己需求的数据库连接池。
  • SmartPool:是一个连接池组件,它模仿应用服务器对象池的特性。SmartPool能够解决一些临界问题如连接泄漏(connection leaks)、连接阻塞、打开的JDBC对象(如Statements、PreparedStatements)等。
  • Druid连接池是阿里巴巴开源的数据库连接池项目。Druid连接池为监控而生,内置强大的监控功能,监控特性不影响性能。功能强大,能防SQL注入,内置Loging能诊断Hack应用行为。

数据库连接池原理

简单来说,数据库连接池就是在一开始先创建N多个数据库连接connection对象,存储在一个集合内,每次使用一个就从集合内取出一个,用完之后又添加到集合内,一直反复使用。

第一步

创建带有是否使用标记的Connection对象。自定义一个带有是否使用标记的Connection对象。

package com.pool;
import java.sql.Connection;
public class PooledConnection {
   
    // 数据库连接
    private Connection conn;
    // 用于标识当前数据库连接的状态 true:执行 false:空闲
    private boolean busy;
    public PooledConnection(Connection conn) {
   
        this.conn = conn;
    }
    public Connection getConn() {
   
        return conn;
    }
    public void setConn(Connection conn) {
   
        this.conn = conn;
    }
    public boolean isBusy() {
   
        return busy;
    }
    public void setBusy(boolean busy) {
   
        this.busy = busy;
    }
}
第二步

创建数据库线程池对象

数据库线程池对象包含默认连接数、最大连接数、每次新增个数、超时时间和连接对象集合。

package com.star;

import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.*;

/**
     * 连接池
     */
public class Poo{
   
    // 初始化连接数
    private static int initSize = 2;
    // 池中最大连接数
    private static int maxSize = 10;
    // 每次创建的连接数
    private static int stepSize = 2;
    // 超时时间
    private static int timeout = 2000;
    // 用来保存创建的数据库连接
    private static List<PoolCollection> poo= Collections.synchronizedList(new ArrayList<>());
    //单例对象
    private static Pooinstance;

    /**
     * 单利模式:无论什么时候只会有一个Pool对象
     */
    private Pool() {
   
        resizePool(initSize);
    }

    /**
     * 单利模式:无论什么时候只会有一个Pool对象
     */
    private Pool() {
   
        resizePool(initSize);
    }

    /**
     * 获取单例对象
     *
     * @return
     */
    public static synchronized PoogetInstance() {
   
        if (instance == null) {
   
            instance = new Pool();
        }
        return instance;
    }

    /**
     * 初始化连接池,循环使用池子里的连接对象,如果不够再创建num个,不超过最大数maxSize
     *
     * @param size 初始时按照initSize给池中添加连接,其他时候按照stepSize给池中加
     */
    private void resizePool(int size) {
   
        int current = pool.size();
        if (current + size > maxSize) {
   
            size = maxSize - current;

        }
        for (int i = 0; i < size; i++) {
   
            pool.add(new PoolCollection(createConnection()));
        }

    }
}
第三步

获取连接

/**
 * 创建连接
 *
 * @return
 */
public Connection createConnection() {
   
    try {
   
        //加载驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        //加载配置文件
        Properties p = new Properties();
        p.load(new FileInputStream("jdbc.properties"));
        return DriverManager.getConnection(p.getProperty("url"), p.getProperty("user"), p.getProperty("password"));
    } catch (Exception e) {
   
        e.printStackTrace();
    }
    return null;
}

/**
 * 获取连接
 *
 * @return
 */
public Connection getConnection() {
   
    Connection connection = getAddConnection();
    while (connection == null) {
   
        System.out.println(Thread.currentThread().getName() + "开始等待获取连接");
        try {
   
            Thread.sleep(timeout);
            connection = getAddConnection();
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        }
    }
    System.out.println(Thread.currentThread().getName() + "拿到连接");
    return connection;
}

/**
 * 可以实现递增获取的机制
 *
 * @return
 */
private Connection getAddConnection() {
   
    Connection connection = getUsableConnection();
    if (connection == null) {
   
        resizePool(stepSize);
        connection = getUsableConnection();
    }
    return connection;
}

/**
 * 获取可用的连接
 *
 * @return
 */
private Connection getUsableConnection() {
   
    Connection connection = null;
    if (pool.size() > 0) {
   
        for (PoolCollection poolCollection : pool) {
   
            if (poolCollection.isUsed() == false) {
   
                poolCollection.setUsed(true);
                connection = poolCollection.getConnection();
                //connection有课能在上次使用过程中产生异常被中断
                try {
   
                    // connection.isValid如果连接未关闭且有效,则返回true
                    if (connection.isValid(2000) == false) {
   
                        connection = createConnection();
                    }
                } catch (SQLException throwables) {
   
                    throwables.printStackTrace();
                }
                break;
            }
        }
    }
    return connection;
}
第四步

归还

/**
 * 关闭
 * @param connection
 */
public void close(Connection connection){
   
    for (PoolCollection poolCollection : pool) {
   
        if(connection == poolCollection.getConnection()){
   
            poolCollection.setUsed(false);
        }
    }
}
第五步

测试

package com.star;

import java.sql.Connection;

public class Main {
   
    public static void main(String[] args) {
   
        for (int i = 0; i < 20; i++) {
   
            new Thread(){
   
                @Override
                public void run() {
   
                    System.out.println(Thread.currentThread().getName() + "获取连接");
                    Poopool1 = Pool.getInstance();
                    Connection connection = pool1.getConnection();
                    try {
   
                        Thread.sleep(10000);
                    } catch (InterruptedException e) {
   
                        e.printStackTrace();
                    }
                    pool1.close(connection);
                    System.out.println(Thread.currentThread().getName() + "释放连接");
                }
            }.start();
            try {
   
                Thread.sleep(200);
            } catch (InterruptedException e) {
   
                e.printStackTrace();
            }
        }
    }
}

DBCP

下载DBCP地址:

下载pool地址

下载Logging地址

DBCP参数

参数 默认值 说明
username 传递给JDBC驱动的用于建立连接的用户名
password 传递给JDBC驱动的用于建立连接的密码
url 传递给JDBC驱动的用于建立连接的URL
driverClassName 使用的JDBC驱动的完整有效的Java 类名
initialSize 0 初始化连接:连接池启动时创建的初始化连接数量,1.2版本后支持
maxActive 8 最大活动连接:连接池在同一时间能够分配的最大活动连接的数量, 如果设置为非正数则表示不限制
maxIdle 8 最大空闲连接:连接池中容许保持空闲状态的最大连接数量,超过的空闲连接将被释放, 如果设置为负数表示不限制
minIdle 0 最小空闲连接:连接池中容许保持空闲状态的最小连接数量,低于这个数量将创建新的连接, 如果设置为0则不创建
maxWait 无限 最大等待时间:当没有可用连接时,连接池等待连接被归还的最大时间(以毫秒计数)超过时间则抛出异常,如果设置为-1表示无限等待
testOnReturn false 是否在归还到池中前进行检验
testWhileIdle false 连接是否被空闲连接回收器(如果有)进行检验.如果检测失败, 则连接将被从池中去除.设置为true后如果要生效,validationQuery参数必须设置为非空字符串
minEvictableIdleTimeMillis 1000 * 60 * 30 连接在池中保持空闲而不被空闲连接回收器线程 (如果有)回收的最小时间值,单位毫秒
numTestsPerEvictionRun 3 在每次空闲连接回收器线程(如果有)运行时检查的连接数量
timeBetweenEvictionRunsMillis -1 在空闲连接回收器线程运行期间休眠的时间值,以毫秒为单位. 如果设置为非正数,则不运行空闲连接回收器线程
validationQuery null SQL查询,用来验证从连接池取出的连接,在将连接返回给调用者之前.如果指定, 则查询必须是一个SQSELECT并且必须返回至少一行记录
testOnBorrow true 是否在从池中取出连接前进行检验,如果检验失败, 则从池中去除连接并尝试取出另一个.

方法1

package com.dbcp;

import javax.sql.DataSource;

import org.apache.commons.dbcp2.BasicDataSource;

public class DBCPUtils {
   
	private String driver = "com.mysql.cj.jdbc.Driver";
	private String ur= "jdbc:mysql://localhost/mydbcp?serverTimezone=GMT";
	private String username = "root";
	private String password = "";
	private static
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

抹泪的知更鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值