用不到200行代码手写一个数据库连接池
设计接口
数据库连接池最主要的功能无非就是获取与数据库的连接,以及用完后还要还回来的方法。
- getConnection( )
- freeConnection(Connection conn)
此外,我们还经常会有想查询数据库状态信息的需求,如获取正在使用中的连接数目,和空闲的连接数目。
- getActiveConnectionNumber( )
- getFreeConnectionNumber( )
当我们需要关闭连接池,需要关闭连接池中所有的连接
- relaseAllConnection( )
编写接口代码
public interface Pool {
//默认最大连接数量
final static Integer DEFAULT_MAX_CONNECTION = 50;
//默认初始化连接数目
final static Integer DEFAULE_INIT_CONNECTION = 10;
//获得连接
Connection getConnection();
//释放连接
boolean freeConnection(Connection conn);
//获取空闲连接数
Integer getFreeNumber();
//获取活动中的连接数
Integer getActiveNumber();
//释放所有连接
boolean relaseAllConnection() throws SQLException;
}
设计实现类
首先考虑到一个程序只需要一个数据库连接池对象就足以满足所有需求,所以数据库连接池的设计应该采用单例模式。
此外就是如何设计数据连接池的初始化。
这里我将数据库连接池的初始化分为了三个步骤,分别是:
- 加载配置文件
- 加载数据库驱动
- 初始化连接池 (向连接池添加连接对象)
直接上完整代码
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Properties;
public class ConnectionPool implements Pool {
String driverName;
String url;
String username;
String password;
Integer maxConnection;
Integer initConnection;
private int activeConnectionNumber;
Deque<Connection> connections;
static Pool pool;
//私有化构造函数,构造连接池对象
private ConnectionPool(String path) {
initProperties(path); //加载并读取配置文件
loadDriver(); //加载驱动
initConnections(); //初始化连接池,向连接池容器中添加连接
}
private void loadDriver(){
try {
Class.forName(this.driverName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private synchronized Connection newConnection() throws SQLException {
Connection connection = DriverManager.getConnection(this.url, this.username, this.password);
return connection;
}
/**
* @param path 只支持properties文件
*/
private void initProperties(String path){
try {
FileInputStream fis = new FileInputStream(path);
Properties properties = new Properties();
properties.load(fis);
initProperties(properties);
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("配置文件不存在");
} catch (IOException e) {
e.printStackTrace();
System.out.println("配置文件读取异常");
}
}
private void initProperties(Properties properties) {
this.url = properties.getProperty("url");
this.driverName = properties.getProperty("driverName");
this.username = properties.getProperty("username");
this.password = properties.getProperty("password");
this.maxConnection = Integer.valueOf(properties.getProperty("maxConnection", String.valueOf(DEFAULT_MAX_CONNECTION)));
this.initConnection = Integer.valueOf(properties.getProperty("initConnection", String.valueOf(DEFAULE_INIT_CONNECTION)));
}
private void initConnections(){
this.connections=new LinkedList<Connection>(); //创建连接池容器
try{
for (int i=0;i<initConnection;i++){ //初始化连接
connections.push(newConnection());
}
}catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 对外提供的静态方法,用于初始化连接池对象
* @param path properties配置文件路径
* @param reset 是否重新初始化连接池
*/
public static void init(String path,boolean reset){
if (reset == true || pool == null){
pool = new ConnectionPool(path);
}
}
public static void init(String path){
init(path,false);
}
/**
* 对外提供的获取连接池的方法
* @return
* @throws Exception
*/
public static Pool getPool() throws Exception {
if(pool == null){
throw new Exception("请先初始化连接池:init(String path)");
}
return pool;
}
/**
* 获取连接,连接池里有空闲连接就返回空闲连接,没有就新建一个链接并返回
* @return
*/
@Override
public synchronized Connection getConnection(){
Connection conn=null;
try{
while(conn==null || conn.isClosed()){
if(!connections.isEmpty()){
conn = connections.pop();
}else if(activeConnectionNumber<maxConnection){
conn = newConnection();
}else {
return null;
}
}
}catch (SQLException e){
e.printStackTrace();
return null;
}
activeConnectionNumber++;
return conn;
}
/**
* 释放连接
* @param conn
* @return
*/
@Override
public synchronized boolean freeConnection(Connection conn) {
connections.push(conn);
activeConnectionNumber--;
return true;
}
/**
* @return 空闲连接数目
*/
@Override
public Integer getFreeNumber() {
return connections.size();
}
/**
* @return 使用中的连接数目
*/
@Override
public Integer getActiveNumber() {
return activeConnectionNumber;
}
/**
* 释放所有连接
* @return
* @throws SQLException
*/
@Override
public boolean relaseAllConnection() throws SQLException {
while(!connections.isEmpty()){
Connection conn = connections.pop();
conn.close();
}
activeConnectionNumber = 0;
return true;
}
}
看一下效果
无放回测试
一次性取出50个连接,中途不释放连接。
测试代码:
public class Test {
public static void main(String[] args) throws SQLException {
ConnectionPool.init("target/classes/JDBC.properties");
Pool pool = null;
try {
pool=ConnectionPool.getPool();
} catch (Exception e) {
e.printStackTrace();
}
for(int i=0;i<50;i++){
long start = System.currentTimeMillis();
Connection connection = pool.getConnection();
long end = System.currentTimeMillis();
String str = String.format("第%d个连接,耗时%d ms",i,end-start);
System.out.println(str);
// pool.freeConnection(connection); //无放回测试
}
pool.relaseAllConnection();
}
}
效果:
有放回测试
public class Test {
public static void main(String[] args) throws SQLException {
ConnectionPool.init("target/classes/JDBC.properties");
Pool pool = null;
try {
pool=ConnectionPool.getPool();
} catch (Exception e) {
e.printStackTrace();
}
for(int i=0;i<50;i++){
long start = System.currentTimeMillis();
Connection connection = pool.getConnection();
long end = System.currentTimeMillis();
String str = String.format("第%d个连接,耗时%d ms",i,end-start);
System.out.println(str);
pool.freeConnection(connection);
}
pool.relaseAllConnection();
}
}