Java手写简单数据库连接池

1. 来源

有时候我们在编写一些简单的需要操作数据库的工具时,可能就用不着复杂的spring、mybatis这样的框架,需要尽量的简单化。从下面的测试数据可以看出,创建数据库连接平均耗时1.8秒,释放连接平均耗时0.2秒,如果不使用连接池,那么每次数据库操作都会多花至少2秒的时间来创建和释放连接。当使用了连接池后,只需要在启动和结束的时候花点时间初始化和释放连接,在中途真正操作数据库时会节省大量的时间,提升系统的性能。

  • 连接耗时测试
==========================开始初始化【源库】连接池===================================
[1/10]加载数据库驱动开始
[1/10]加载数据库驱动完成
[1/10]获取数据库连接开始
[1/10]获取数据库连接完成,耗时:2658
[2/10]加载数据库驱动开始
[2/10]加载数据库驱动完成
[2/10]获取数据库连接开始
[2/10]获取数据库连接完成,耗时:1389
[3/10]加载数据库驱动开始
[3/10]加载数据库驱动完成
[3/10]获取数据库连接开始
[3/10]获取数据库连接完成,耗时:1401
[4/10]加载数据库驱动开始
[4/10]加载数据库驱动完成
[4/10]获取数据库连接开始
[4/10]获取数据库连接完成,耗时:1297
[5/10]加载数据库驱动开始
[5/10]加载数据库驱动完成
[5/10]获取数据库连接开始
[5/10]获取数据库连接完成,耗时:2598

......中间过程省略......

正在等待关闭连接池,释放数据库连接...
成功释放【源库】第1/10个连接,单步耗时:198ms,总耗时:198ms
成功释放【源库】第2/10个连接,单步耗时:175ms,总耗时:373ms
成功释放【源库】第3/10个连接,单步耗时:235ms,总耗时:608ms
成功释放【源库】第4/10个连接,单步耗时:187ms,总耗时:795ms
成功释放【源库】第5/10个连接,单步耗时:200ms,总耗时:995ms

3. 连接池源码

package com.open.data.migrate.source.to.target.pool;

import com.open.data.migrate.source.to.target.config.DbConfig;
import com.open.data.migrate.source.to.target.util.DbUtils;
import com.open.data.migrate.source.to.target.util.LogUtils;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.concurrent.ArrayBlockingQueue;

/**
 * @author 2810010108@qq.com
 * @project open-component-migrate-to-mysql
 * @package com.open.component.convert.column.encoding.pool
 * @date 2020/9/10 15:45
 * @description 简单连接池,源库和目标库分别创建各自的连接池,连接池使用阻塞队列实现
 **/
public final class ConnectionPool {

    /**
     * 源库连接池,默认容量为100,可以通过其他方式支持可配
     */
    private static ArrayBlockingQueue<Connection> SOURCE_CONNECTION_POOL = new ArrayBlockingQueue<Connection>(100);
    /**
     * 目标库连接池,默认容量为100,可以通过其他方式支持可配
     */
    private static ArrayBlockingQueue<Connection> TARGET_CONNECTION_POOL = new ArrayBlockingQueue<Connection>(100);

    static {
        // 类加载时初始化数据库连接池
        initConnectionPool();
    }

    /**
     * 初始化数据库连接池方法,通过DbUtils从配置文件读取数据库连接信息,创建N个数据库连接存入队列,N为配置的数据库连接数
     */
    private static void initConnectionPool() {
        DbConfig sourceDbConfig = DbUtils.getSourceDbConfig();
        LogUtils.printInfoLog("==========================开始初始化【源库】连接池===================================");
        for (int i = 0; i < sourceDbConfig.getConnections(); i++) {
            Connection connection = initConnection(sourceDbConfig, i + 1);
            SOURCE_CONNECTION_POOL.add(connection);
        }
        LogUtils.printInfoLog("==========================结束初始化【源库】连接池===================================");
        LogUtils.printInfoLog("==========================开始初始化【目标库】连接池===================================");
        DbConfig targetDbConfig = DbUtils.getTargetDbConfig();
        for (int i = 0; i < targetDbConfig.getConnections(); i++) {
            Connection connection = initConnection(targetDbConfig, i + 1);
            TARGET_CONNECTION_POOL.add(connection);
        }
        LogUtils.printInfoLog("==========================结束初始化【目标库】连接池===================================");
    }

    /**
     * 从源库连接池中阻塞的取得一个连接,队列的take()方法为阻塞式出队
     *
     * @return 源库连接
     */
    public static Connection borrowSource() {
        try {
            return SOURCE_CONNECTION_POOL.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 数据库操作完毕,阻塞的归还源库连接到连接池,队列的put操作为阻塞式入队
     *
     * @param connection 要归还的源库连接
     */
    public static void returnSource(Connection connection) {
        try {
            SOURCE_CONNECTION_POOL.put(connection);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 从目标库连接池中阻塞的取得一个连接,队列的take()方法为阻塞式出队
     *
     * @return 源库连接
     */
    public static Connection borrowTarget() {
        try {
            return TARGET_CONNECTION_POOL.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 数据库操作完毕,阻塞的归还目标库连接到连接池,队列的put操作为阻塞式入队
     *
     * @param connection 要归还的源库连接
     */
    public static void returnTarget(Connection connection) {
        try {
            TARGET_CONNECTION_POOL.put(connection);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * @param config 数据库连接信息对象
     * @param number 序号,用于打印正在初始化的数据库连接序号
     * @return 创建完成的数据库连接
     */
    private static Connection initConnection(DbConfig config, int number) {
        Connection connection = null;
        try {
            //加载数据驱动
            LogUtils.printInfoLog(String.format("[%s/%s]加载数据库驱动开始", number, config.getConnections()));
            Class.forName(config.getDriver());
            LogUtils.printInfoLog(String.format("[%s/%s]加载数据库驱动完成", number, config.getConnections()));
            // 连接数据库
            LogUtils.printInfoLog(String.format("[%s/%s]获取数据库连接开始", number, config.getConnections()));
            connection = DriverManager.getConnection(config.getUrl(), config.getUsername(), config.getPassword());
            LogUtils.printInfoLog(String.format("[%s/%s]获取数据库连接完成", number, config.getConnections()));
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            LogUtils.printErrorLog(String.format("[%s/%s]加载数据库驱动失败", number, config.getConnections()));
        } catch (Exception e) {
            e.printStackTrace();
            LogUtils.printErrorLog(String.format("[%s/%s]连接数据库失败", number, config.getConnections()));
        }
        return connection;
    }

    /**
     * 关闭所有的源库和目标库的连接
     */
    public static void closeAllConnections() {
        close(SOURCE_CONNECTION_POOL, "源库");
        close(TARGET_CONNECTION_POOL, "目标库");

    }

    /**
     * 释放传入连接池中所有的连接,避免数据库连接被恶意占用
     *
     * @param pool 要关闭的连接池
     * @param type 库类别标识,用于打印日志
     */
    public static void close(ArrayBlockingQueue<Connection> pool, String type) {
        int size = pool.size();
        int counter = 0;
        long start = System.currentTimeMillis();
        while (true) {
            long startInner = System.currentTimeMillis();
            Connection connection = pool.poll();
            if (connection == null) {
                break;
            }
            try {
                connection.close();
                LogUtils.printInfoLog(String.format("成功释放【%s】第%s/%s个连接,单步耗时:%sms,总耗时:%sms", type, ++counter, size, System.currentTimeMillis() - startInner, System.currentTimeMillis() - start));
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}


3. 出处

该连接池来自于数据库迁移工具,该工具无任何的框架,纯JDK实现,jar运行。可以实现的数据流向有:

  • mysql->mysql
  • mysql->oracle
  • oracle->oracle
  • oracle->mysql
    还可以通过引入驱动包,扩展SourceDbEnums和TargetDbEnums枚举类,可以实现其他数据库支持

项目地址:https://gitee.com/mengbaoyuxuan/open-data-migrate-source-to-target.git

交流QQ群:566654343

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
以下是Java手写动态数据库连接池的示例代码: ``` import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; public class ConnectionPool { private String url; private String username; private String password; private List<Connection> connections = new ArrayList<>(); private int maxConnections; public ConnectionPool(String url, String username, String password, int maxConnections) { this.url = url; this.username = username; this.password = password; this.maxConnections = maxConnections; } public synchronized Connection getConnection() throws SQLException { if (connections.isEmpty()) { if (maxConnections == 0 || connections.size() < maxConnections) { Connection conn = DriverManager.getConnection(url, username, password); connections.add(conn); return conn; } else { throw new SQLException("Connection pool is full"); } } else { Connection conn = connections.remove(0); if (conn.isClosed()) { return getConnection(); } else { return conn; } } } public synchronized void releaseConnection(Connection conn) { connections.add(conn); } public synchronized void closeAllConnections() throws SQLException { for (Connection conn : connections) { conn.close(); } connections.clear(); } } ``` 使用方法: ``` ConnectionPool connectionPool = new ConnectionPool("jdbc:mysql://localhost:3306/mydb", "user", "password", 10); Connection conn = connectionPool.getConnection(); // Use the connection for database operations connectionPool.releaseConnection(conn); connectionPool.closeAllConnections(); ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

羽轩GM

您的鼓励是我创作的动力

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

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

打赏作者

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

抵扣说明:

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

余额充值