JDBC中提供了一个接口DataSource,所有的连接池都必须实现这个接口

自定义数据库连接池

创建一个连接池,继承DataSource接口并实现方法,其中只需要关注getConnection()一个方法即可,用于获取连接,然后再创建一个将连接 放回连接池的方法

package com.robot.utils;

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.concurrent.ConcurrentLinkedQueue;
import java.util.logging.Logger;

/**
 * 自定义数据库连接池。
 *
 * @author 张宝旭
 */
public class MyDbUtilsPool implements DataSource {
    /**
     * 创建容器,保存连接
     */
    private static ConcurrentLinkedQueue<Connection> queue = new ConcurrentLinkedQueue<>();

    static {
        try {
            // 加载驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            String url = "jdbc:mysql://localhost:3306/gp2002?useSSL=false&characterEncoding=utf8&serverTimezone=GMT%2B8";
            // 创建多个连接并存放到容器中
            for(int i = 0; i < 5; i++) {
                Connection connection = DriverManager.getConnection(url, "bao", "123456");
                queue.offer(connection);
            }
            System.out.println("连接池初始化成功");
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取连接。
     * @return 连接
     */
    @Override
    public Connection getConnection() {
        Connection connection = null;
        synchronized (queue) {
            if (queue.size() > 0) {
                connection = queue.poll();
            }
        }
        return connection;
    }

    /**
     * 将连接放回连接池。
     *
     * @param connection 归还的连接
     */
    public void release(Connection connection) {
        queue.offer(connection);
    }

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

测试

package com.robot.test;

import com.robot.utils.MyDbUtilsPool;
import org.junit.Test;

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

/**
 * @author 张宝旭
 */
public class MyDbUtilsPoolTest {
    @Test
    public void myPoolTest() throws SQLException {
        MyDbUtilsPool myDbUtilsPool = new MyDbUtilsPool();
        
        // 获取10次连接
        for(int i = 0; i < 10; i++) {
            Connection connection = myDbUtilsPool.getConnection();
            if (connection != null) {
                System.out.println("获取连接: " + connection.hashCode());
                myDbUtilsPool.release(connection);
            } else {
                System.out.println("没有获取到连接!");
            }
        }
        
    }
}

数据库连接池中共有5个连接,如果有10个获取连接而不放回,就会出现后5个获取不到连接的情况,所以每次获取连接使用完之后,需要将连接放回连接池中



Druid连接池

  • 阿里巴巴开发
  • 支持所有JDBC兼容的数据库
  • 简单SQL语句用时10微秒以内
  • 需要手动导入druid-1.1.5.jar包

测试Druid连接池

package com.robot.test;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;
import org.junit.Test;

import java.sql.SQLException;

/**
 * 测试Druid连接池。
 *
 * @author 张宝旭
 */
public class DruidPoolTest {
    @Test
    public void druidTest() throws SQLException {
        // 1 创建连接池对象
        DruidDataSource dataSource = new DruidDataSource();
        // 2 配置参数
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/gp2002?useSSL=false&characterEncoding=utf8&serverTimezone=GMT%2B8");
        dataSource.setUsername("bao");
        dataSource.setPassword("123456");
        // 其它参数
        // 初始化大小
        dataSource.setInitialSize(10);
        // 最大值
        dataSource.setMaxActive(50);
        // 最小空闲
        dataSource.setMinIdle(5);
        // 最大等待时间
        dataSource.setMaxWait(5000);

        // 3 使用
        for(int i = 0; i < 100; i++) {
            DruidPooledConnection connection = dataSource.getConnection();
            System.out.println("连接: " + connection.toString());
            connection.close(); // 将连接返回连接池
        }
    }
}

以上为测试Druid,接下来也为这个连接池写一个工具类

先创建一个配置文件druid.properties,将连接池的配置参数写到配置文件中

driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/gp2002?useSSL=false&characterEncoding=utf8&serverTimezone=GMT%2B8
username=bao
password=123456

initialSize=10
maxActive=50
minIdle=5
maxWait=5000

然后创建工具类

只是获取连接的方式变成了从连接池中获取,工具类中的其它方法还是不变的

package com.robot.utils;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

/**
 * @author 张宝旭
 */
public class DbPool {

    // 创建连接池
    private static DataSource dataSource = null;
    // 创建线程局部变量
    private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
    static {
        try {
            Properties properties = new Properties();
            InputStream resourceAsStream = DbPool.class.getClassLoader().getResourceAsStream("druid.properties");
            properties.load(resourceAsStream);
            // 初始化连接池
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            System.out.println("连接池初始化失败");
        }
    }

    /**
     * 获取连接。
     *
     * @return 连接
     */
    public static Connection getConnection() {
        Connection connection = threadLocal.get();
        if (connection == null) {
            try {
                connection = dataSource.getConnection();
                threadLocal.set(connection);
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException("连接失败");
            }
        }
        return connection;
    }

    /**
     * 释放资源
     * @param connection 连接
     * @param preparedStatement 执行SQL对象
     * @param resultSet 结果集
     */
    public static void close(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet) {
        try {
            if (connection != null) {
                // 判断有没有开启事务
                if (!threadLocal.get().getAutoCommit()) {
                    connection.close();
                    threadLocal.remove();
                }
            }
            if (preparedStatement != null) {
                preparedStatement.close();
            }
            if (resultSet != null) {
                resultSet.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

使用数据库连接池可以减少创建连接带来的消耗,提高效率