数据库连接池

阅读本文手写一套数据库连接池,您可能需要了解如下几个知识点:

  1. 数据库连接池的原理及作用

  2. 并发队列介绍及使用

  3. 配置文件properties信息映射到Java对象

在我们配置连接池的时候,会配置一些数据,比如最小空闲连接数,最大空闲连接数等等,本文中,您需要理解如下几个概念。

「空闲连接池」:用来存放已经被创建,但是未被使用的连接的容器。
「活动连接池」:用来存放已经被创建,并且被使用的连接的容器。
「最大空闲数」:空闲连接池中,最多存在的空闲连接数量。
「初始化连接数」:第一次加载的时候,需要创建的连接数量,一般大于最小空闲数,小于最大空闲数。
「最大连接数」:空闲连接和活动连接之和。

「1、初始化」:第一次加载的时候,根据配置的初始化连接数,创建连接,将创建的连接放入到空闲连接池;
「2、获取连接」:优先从空闲池获取,如果空闲池没有,就创建一个新的连接

优先从空闲池获取

 3、「释放连接」:将需要释放的连接从活动连接池中移除,如果空闲连接池没有满,放将移除的连接放入到空闲连接池,如果空闲连接池已经满了,则关闭此链接。

代码结构如下

连接池配置文件

##驱动名称:不推荐com.mysql.jdbc.Driver,而是推荐使用com.mysql.cj.jdbc.Driver
driverName = com.mysql.cj.jdbc.Driver
##数据库连接地址
url = jdbc:mysql://127.0.0.1:3306/test
userName = root
passWord = root
##连接池名字
poolName = Hutao Connection Pool
##空闲池,最小连接数
minFreeConnections = 1
##初始化空闲池连接数
initFreeConnections = 3
##空闲池,最大连接数
maxFreeConnections = 5
##最大允许的连接数,一般小于数据库总连接数
maxConnections = 15
##重试获得连接的频率  一秒
retryConnectionTimeOut = 1000
##连接超时时间,默认20分钟  1000 * 60 * 20
connectionTimeOut = 1200000

* @Description:数据库连接池属性信息
 */
public class DbProperties {

    /* 链接属性 */
    private String driverName;

    private String url;

    private String userName;

    private String passWord;

    private String poolName;

    /**
     * 空闲池,最小连接数
     */
    private int minFreeConnections;

    /**
     * 空闲池,最大连接数
     */
    private int maxFreeConnections;

    /**
     * 初始连接数
     */
    private int initFreeConnections;

    /**
     * 重试获得连接的频率  毫秒
     */
    private long retryConnectionTimeOut;

    /**
     * 最大允许的连接数
     */
    private int maxConnections;

    /**
     * 连接超时时间
     */
    private long connectionTimeOut;

    public String getDriverName() {
            return driverName;
    }

    public void setDriverName(String driverName) {
            this.driverName = driverName;
    }

    public String getUrl() {
            return url;
    }

    public void setUrl(String url) {
            this.url = url;
    }

    public String getUserName() {
            return userName;
    }

    public void setUserName(String userName) {
            this.userName = userName;
    }

    public String getPassWord() {
            return passWord;
    }

    public void setPassWord(String passWord) {
            this.passWord = passWord;
    }

    public String getPoolName() {
            return poolName;
    }

    public void setPoolName(String poolName) {
            this.poolName = poolName;
    }

    public int getMinFreeConnections() {
            return minFreeConnections;
    }

    public void setMinFreeConnections(int minFreeConnections) {
            this.minFreeConnections = minFreeConnections;
    }

    public int getMaxFreeConnections() {
            return maxFreeConnections;
    }

    public void setMaxFreeConnections(int maxFreeConnections) {
            this.maxFreeConnections = maxFreeConnections;
    }

    public int getInitFreeConnections() {
            return initFreeConnections;
    }

    public void setInitFreeConnections(int initFreeConnections) {
            this.initFreeConnections = initFreeConnections;
    }

    public long getRetryConnectionTimeOut() {
            return retryConnectionTimeOut;
    }

    public void setRetryConnectionTimeOut(long retryConnectionTimeOut) {
            this.retryConnectionTimeOut = retryConnectionTimeOut;
    }

    public int getMaxConnections() {
            return maxConnections;
    }

    public void setMaxConnections(int maxConnections) {
            this.maxConnections = maxConnections;
    }

    public long getConnectionTimeOut() {
            return connectionTimeOut;
    }

    public void setConnectionTimeOut(long connectionTimeOut) {
            this.connectionTimeOut = connectionTimeOut;
    }

    @Override
    public String toString() {
            return "DbProperties [driverName=" + driverName + ", url=" + url + ", userName=" + userName + ", passWord="
                            + passWord + ", poolName=" + poolName + ", minFreeConnections=" + minFreeConnections
                            + ", maxFreeConnections=" + maxFreeConnections + ", initFreeConnections=" + initFreeConnections
                            + ", retryConnectionTimeOut=" + retryConnectionTimeOut + ", maxConnections=" + maxConnections
                            + ", connectionTimeOut=" + connectionTimeOut + "]";
    }
}

连接池管理

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.util.Enumeration;
import java.util.ResourceBundle;

import com.hutao.pool.database.pojo.DbProperties;
import com.hutao.pool.database.service.DbPoolService;
import com.hutao.pool.database.service.impl.DbPoolServiceImpl;

/**
 * @Description:数据库连接池管理
 */
public class DbPoolManager {

    private static String sourcePath = "com/hutao/resources/database";

    /**
     * 数据库连接池配置属性
     */
    private static DbProperties properties = null;

    /**
     * 数据库连接池接口
     */
    private static DbPoolService connectionPool = null;


    /**
     * 双重检查机制静态加载连接池
     */
    static {
            try {
                    if(properties == null) {
                            synchronized(DbPoolManager.class) {
                                    if(properties == null) {
                                            properites2Object();
                                            connectionPool = new DbPoolServiceImpl(properties);
                                    }
                            }
                    }
            } catch (Exception e) {
                    e.printStackTrace();
            }

    }

    /**
     * @Description:数据库连接池database配置文件映射到java对象
     */
    private static void properites2Object() throws NoSuchFieldException, IllegalAccessException {
            properties = new DbProperties();
            ResourceBundle resourceBundle = ResourceBundle.getBundle(sourcePath);
            //获取资源文件中所有的key
            Enumeration<String> keys = resourceBundle.getKeys();
            while (keys.hasMoreElements()) {
                    String key = (String) keys.nextElement();
                    //反射获取类中的属性字段
                    Field field= DbProperties.class.getDeclaredField(key);
                    //属性字段的类型
                    Type genericType = field.getGenericType();
                    //属性设置可访问
                    field.setAccessible(true);
                    //根据key读取对应的value值
                    String value = resourceBundle.getString(key);
                    if("int".equals(genericType.getTypeName())) {
                            //反射给属性赋值
                            field.set(properties, Integer.parseInt(value));
                    }else if("long".equals(genericType.getTypeName())) {
                            field.set(properties, Long.parseLong(value));
                    }else if("java.lang.String".equals(genericType.getTypeName())) {
                            field.set(properties,value);
                    }
            }
    }

    /**
     * @Description:获取连接
     */
    public static Connection getConnection() {
            return connectionPool.getConnection();
    }

    /**
     * @Description:释放连接
     */
    public static void releaseConnection(Connection connection) {
            connectionPool.releaseConnection(connection);
    }

连接池接口

import java.sql.Connection;

/**
 * @Description:数据库连接池
 */
public interface DbPoolService {
 
    /**
     * @Description:判断连接是否可用,可用返回true
     */
    public boolean isAvailable(Connection connection);

    /**
     * @Description:使用重复利用机制获取连接
     */
    public Connection getConnection();

    /**
     * @Description:使用可回收机制释放连接
     */
    public void releaseConnection(Connection connection);
}

连接池实现

import java.sql.Connection;
import java.sql.DriverManager;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import com.hutao.pool.database.pojo.DbProperties;
import com.hutao.pool.database.service.DbPoolService;

/**
 * @Description:数据库连接池实现
 */
public class DbPoolServiceImpl implements DbPoolService {
 
    /**
     * 存放空闲连接的容器,除了可以使用并发队列,也可以使用线程安全的集合Vector
     */
    private BlockingQueue<Connection> freeConnection = null;
    /**
     * 存放活动连接的容器,除了可以使用并发队列,也可以使用线程安全的集合Vector
     */
    private BlockingQueue<Connection> activeConnection = null;

    /**
     * 存放映射的属性配置文件
     */
    private DbProperties dDbProperties;


    public DbPoolServiceImpl(DbProperties dDbProperties) throws Exception {
        // 获取配置文件信息
        this.dDbProperties = dDbProperties;
        freeConnection =  new LinkedBlockingQueue<>(dDbProperties.getMaxFreeConnections());
        activeConnection = new LinkedBlockingQueue<>(dDbProperties.getMaxConnections());
        init();
    }

    /**
     * @Description:初始化空闲线程池
     */
    private void init() throws Exception {
        System.out.println("初始化线程池开始,线程池配置属性:"+dDbProperties);
        if (dDbProperties == null) {
                throw new  Exception("连接池配置属性对象不能为空");
        }
        //获取连接池配置文件中初始化连接数
        for (int i = 0; i < dDbProperties.getInitFreeConnections(); i++) {
                //创建Connection连接
                Connection newConnection = newConnection();
                if (newConnection != null) {
                        //将创建的新连接放入到空闲池中
                        freeConnection.add(newConnection);
                }
        }
        System.out.println("初始化线程池结束,初始化线程数:"+dDbProperties.getInitFreeConnections());
    }

    private synchronized Connection newConnection() {
        try {
                Class.forName(dDbProperties.getDriverName());
                return DriverManager.getConnection(dDbProperties.getUrl(), dDbProperties.getUserName(),dDbProperties.getPassWord());
        } catch (Exception e) {
                e.printStackTrace();
                return null;
        }
    }

    /**
     * @Description:判断连接是否可用,可用返回true
     */
    @Override
    public boolean isAvailable(Connection connection) {
        try {
                if (connection == null || connection.isClosed()) {
                        return false;
                }
        } catch (Exception e) {
                e.printStackTrace();
        }
        return true;
    }

    /**
     * @Description:使用重复利用机制获取连接:如果总连接未超过最大连接,则从空闲连接池获取连接或者创建一个新的连接,如果超过最大连接,则等待一段时间以后,继续获取连接
     */
    @Override
    public synchronized Connection getConnection() {
        Connection connection = null;
        //空闲连接和活动连接的总数加起来 小于 最大配置连接
        System.out.println("当前空闲连接总数:"+freeConnection.size()+" 当前活动连接总数"+activeConnection.size()+", 配置最大连接数:"+ dDbProperties.getMaxConnections());
        if (freeConnection.size()+activeConnection.size() < dDbProperties.getMaxConnections()) {
                //空闲连接池,是否还有还有连接,有就取出来,没有就创建一个新的。
                if (freeConnection.size() > 0) {
                        connection = freeConnection.poll();
                        System.out.println("从空闲线程池取出线程:"+connection+"当前空闲线程总数:"+freeConnection.size());
                } else {
                        connection = newConnection();
                        System.out.println("空闲连接池没有连接,创建连接"+connection);
                }
                //拿到的连接可用,就添加活动连接池,否则就递归继续找下一个
                boolean available = isAvailable(connection);
                if (available) {
                        activeConnection.add(connection);
                } else {
                        connection = getConnection();
                }

        } else {
                System.out.println("当前连接数已达到最大连接数,等待"+dDbProperties.getRetryConnectionTimeOut()+"ms以后再试");
                try {
                        wait(dDbProperties.getRetryConnectionTimeOut());
                } catch (InterruptedException e) {
                        e.printStackTrace();
                }
                connection = getConnection();
        }
        return connection;
    }

    /**
     * @Description:使用可回收机制释放连接:如果连接可用,并且空闲连接池没有满,则把连接归还到空闲连接池,否则关闭连接
     */
    @Override
    public synchronized void releaseConnection(Connection connection) {
        try {
            if (isAvailable(connection) && freeConnection.size() < dDbProperties.getMaxFreeConnections()) {
                    freeConnection.add(connection);
                    System.out.println("空闲线程池未满,归还连接"+connection);

            } else {
                    connection.close();
                    System.out.println("空闲线程池已满,关闭连接"+connection);
            }
            activeConnection.remove(connection);
            notifyAll();
        } catch (Exception e) {
                e.printStackTrace();
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值