手写数据库连接池(java)

时间:2024-04-06 18:22:28

手写数据库连接池(java)

我前几天学习了数据库连接池的基本内容,自己尝试着写了一个连接池并导出jar包使用,虽然实现了连接池的基本功能,但也存在一些问题。
以下是我连接池的目录结构:
手写数据库连接池(java)
配置文件内容:

#驱动路径
driver=com.mysql.jdbc.Driver
#JDBC连接URL
url=jdbc:mysql://127.0.0.1:3306/exercise?characterEncoding=utf8&useSSL=true
#账号
username=root
#密码
password=root
#初始连接池大小
initPoolSize=10
#最小连接数
minPoolSize=10
#最大连接数
maxPoolSize=50
#最大空闲时间
maxIdleTime=100000
#默认自动增长连接数
defaultGrow=5

连接池的三个主要实现类的代码:

package myConnectionPool;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 * 连接类
 * 保存单个数据库连接
 * 记录该连接开始空闲时间和目前空闲时间
 */
public class WSConnection {
    /**
     * 数据库连接
     */
    private Connection connection=null;
    /**
     * 开始空闲时间
     */
    private long startIdleTime;
    /**
     * 目前空闲时间
     */
    private long nowIdleTime;

    /**
     * 构造函数
     * 获得数据库连接和该连接的开始空闲时间
     */
    public WSConnection(String driver, String url, String username, String password){
        try {
            Class.forName(driver);
            connection = DriverManager.getConnection(url, username, password);
            startIdleTime=System.currentTimeMillis();
        } catch (Exception e) {
            System.out.println("连接获取失败!");
            System.exit(0);
        }
    }

    public WSConnection(Connection connection){
        this.connection=connection;
        startIdleTime=System.currentTimeMillis();
    }

    /**
     * 提供获得数据库连接的方法
     */
    public Connection getConnection() {
        return connection;
    }

    /**
     * 提供设置开始空闲时间的方法
     */
    public void setStartIdleTime() {
        this.startIdleTime = System.currentTimeMillis();
    }

    /**
     * 提供获得目前空闲时间的方法
     */
    public long getNowIdleTime() {
        nowIdleTime=System.currentTimeMillis()-this.startIdleTime;
        return nowIdleTime;
    }

    /**
     * 提供关闭数据库连接的方法
     */
    public void closeConnection(){
        try {
            connection.close();
        } catch (SQLException e) {
            System.out.println("连接关闭异常!");
            System.exit(0);
        }
    }
}

package myConnectionPool;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.util.LinkedList;
import java.util.Properties;

/**
 * 连接池类
 * 管理数据库连接
 *
 * 实现业务逻辑:
 *
 * -若有用户请求连接,则将空闲连接链表的头结点从链表中移出,让用户使用
 * -若有用户关闭连接,则重置该连接开始空闲时间,并将其加入空闲连接链表队尾
 *
 * -没有空闲连接时,主动创建一定数目空闲连接,但要保证总连接数不超过最大连接数
 * -当总连接数小于最小连接数时,主动创建一定数目空闲连接,保证总连接数不超过最大连接数
 *
 * -当总连接数大于最大连接数时,关闭空闲连接
 * -当连接的目前空闲时间大于最大空闲时间时,关闭该连接
 *
 * -当连接池已满,仍有用户请求连接时,先让其等待最大空闲时间,如果之后连接池仍然无空闲连接时,报告获取连接失败,请稍后重试错误
 */
public class WSConnectionPool {
    /**
     *属性1:驱动路径,从配置文件获取
     */
    private String driver;
    /**
     *属性2:DBC连接URL,从配置文件获取
     */
    private String url;
    /**
     *属性3:数据库账号,从配置文件获取
     */
    private String username;
    /**
     *属性4:数据库密码,从配置文件获取
     */
    private String password;
    /**
     *属性5:初始数据库连接数,从配置文件获取
     */
    private int initPoolSize;
    /**
     *属性6:最小数据库连接数,从配置文件获取
     */
    private int minPoolSize;
    /**
     *属性7:最大数据库连接数,从配置文件获取
     */
    private int maxPoolSize;
    /**
     *属性8:数据库连接最大空闲时间,从配置文件获取
     */
    private int maxIdleTime;
    /**
     *属性9:目前的数据库连接数(包括空闲数据库连接和已分配数据库连接)
     */
    private int nowPoolSize=0;
    /**
     *属性10:数据库空闲连接的集合,LinkedList集合:add()尾插返回Boolean,pop()头删返回Connection
     */
    private LinkedList <WSConnection> pool= new LinkedList <WSConnection>();
    /**
     *属性11:默认自动增长连接数,从配置文件获取
     */
    private int defaultGrow;

    /**
     * 构造方法,为11个属性设置初始值
     */
    public WSConnectionPool(){
        try {
            /**
             * 加载配置文件
             */
            Properties properties = new Properties();
            InputStream resourceAsStream =WSConnectionPool.class.getClassLoader().getResourceAsStream("wsConnectionPool.properties");
            properties.load(resourceAsStream);
            /**
             * 从配置文件中读取相关属性值
             */
            driver=properties.getProperty("driver");
            url=properties.getProperty("url");
            username=properties.getProperty("username");
            password=properties.getProperty("password");
            initPoolSize=Integer.parseInt(properties.getProperty("initPoolSize"));
            minPoolSize=Integer.parseInt(properties.getProperty("minPoolSize"));
            maxPoolSize=Integer.parseInt(properties.getProperty("maxPoolSize"));
            maxIdleTime=Integer.parseInt(properties.getProperty("maxIdleTime"));
            defaultGrow= Integer.parseInt(properties.getProperty("defaultGrow"));
            /**
             * 数据库连接池初始化配置检查
             */
            /**
             * 1.检查最大空闲时间是否大于零
             */
            if(maxIdleTime<=0){
                throw new Exception();
            }
            /**
             * 2.检查最小连接数是否小于等于最大连接数
             */
            if(minPoolSize>maxPoolSize){
                throw new Exception();
            }
            /**
             * 3.检查初始连接数是否小于等于最大连接数,是否大于等于最小连接数
             */
            if(initPoolSize<minPoolSize||initPoolSize>maxPoolSize){
                throw new Exception();
            }
            /**
             * 根据从配置文件中得到的属性值设置其他属性:
             * 创建初始连接数个数据库连接,并更新目前的数据库连接数
             */
            growConnection(initPoolSize);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            System.out.println("配置信息错误,连接池初始化失败!");
            System.exit(0);
        }
    }

    /**
     * 创建一定数目的空闲连接的方法
     */
    /**
     * 创建一个空闲连接
     */
    private void growConnection(){
        WSConnection wsConnection = new WSConnection(driver, url, username, password);
        pool.add(wsConnection);
        addNowConnection();
    }

    /**
     * 创建指定数目的空闲连接
     */
    private void growConnection(int time){
        for (int i = 0; i < time; i++) {
            growConnection();
        }
    }

    /**
     * 连接池关闭连接的方法
     */
    /**
     * 关闭指定的数据库连接
     */
    private void closeConnection(WSConnection wsConnection){
        wsConnection.closeConnection();
        deleteNowConnection();
    }

    /**
     * 检查空闲连接链表,查找目前空闲时间大于最大空闲时间的连接并关闭
     */
    private void closeIdleConnection(){
        WSConnection wsConnection=null;
        for (int i = 0; i < pool.size(); i++) {
            wsConnection=pool.get(i);
            if(wsConnection.getNowIdleTime()>maxIdleTime){
                pool.remove(i);
                i--;
                closeConnection(wsConnection);
            }
        }
    }

    /**
     * 关闭已分配给用户的连接(实际上是将该连接尾插入空闲连接链表,并重置该连接的开始空闲时间)
     */
    private void closeUsingConnection(WSConnection wsConnection){
        pool.add(wsConnection);
        wsConnection.setStartIdleTime();
    }

    /**
     * 处理目前连接数和空闲连接数的方法
     */
    /**
     * 增加目前连接数,如果目前连接数大于最大连接数,释放连接
     */
    private void addNowConnection(){
        nowPoolSize++;
        while(nowPoolSize>maxPoolSize){
            closeIdleConnection();
        }
    }
    /**
     * 减少目前连接数,如果目前连接数小于最小连接数,建立自动增长连接数个空闲连接
     */
    private void deleteNowConnection(){
        nowPoolSize--;
        if(nowPoolSize<minPoolSize){
            growConnection(defaultGrow);
        }
    }
    /**
     * 检测空闲连接数,如果空闲连接为零,创建自动增长连接数个空闲连接
     */
    private void isHaveIdleConnection(){
        if(pool.size()==0){
            growConnection(defaultGrow);
        }
    }

    /**
     * 用户请求连接的方法
     */
    public Connection getConnection(){
        Connection connection=null;
        if(nowPoolSize==maxPoolSize&&pool.size()==0){
            try {
                Thread.sleep(maxIdleTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        if(pool.size()==0){
            System.out.println("获取连接失败,请稍后重试!");
            System.exit(0);
        }
        connection=pool.pop().getConnection();
        return connection;
    }
    /**
     * 用户释放连接的方法
     */
    public void releaseConnection(Connection connection){
        closeUsingConnection(new WSConnection(connection));
    }

}

package myConnectionPool;

import java.sql.Connection;

/**
 * 连接池管理类
 * 含有唯一的连接池类
 * 只负责连接的获取和释放
 */
public class WSConnectionPoolManage {
    /**
     * 唯一的连接池类
     */
    private static WSConnectionPool connectionPool=new WSConnectionPool();

    /**
     * 获取数据库连接的方法
     */
    public static synchronized Connection getConnection() {
        return connectionPool.getConnection();
    }

    /**
     * 释放数据库连接的方法
     */
    public static synchronized void releaseConnection(Connection connection) {
        connectionPool.releaseConnection(connection);
    }
    /**
     * 主方法
     */
    public static void main(String[] args) {
        WSConnectionPoolManage connectionPoolManage = new WSConnectionPoolManage();
        System.out.println("连接池创建成功!");
    }
}

jar包使用测试:
jar包执行测试:
手写数据库连接池(java)手写数据库连接池(java)一个客户连接测试:
手写数据库连接池(java)手写数据库连接池(java)多个客户连接测试:

测试代码:
手写数据库连接池(java)
测试结果:
手写数据库连接池(java)
大量测试结果分析:
当连接用户数目较少时,所有用户均可获取连接,但用户数目较大时,有些用户无法获取连接。

该连接池存在问题:
1.配置文件信息在jar包中固定了,使用jar包时不能修改,使得jar包通用性不强。
2.用户获取和释放连接方法均加了锁,使得连接池并发度不高。
3.在目前数据库连接数发生变化时才进行连接池维护,不能自动维护连接池。