package com.flsoft.db;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* 初学数据库连接池
*
* @author FL-soft
* @version 0.1
*/
public class ConnectionPool {
private String jdbcDriver = " "; // jdbc 驱动
private String dbURL = ""; // Datebase的URL
private String username = "";
private String password = "";
private String testTable = "userinfo"; // 测试连接是否可用的测试表名,默认没有测试表
private int initConnections = 10; // 连接池的初始大小
private int incrementalConnections = 5; // 连接池自动增加的大小
private int maxConnections = 50; // 连接池的连接上限
/*
* 存放连接池中连接的List集合 存放的对象为PooledConnection型
*/
@SuppressWarnings("unchecked")
private List connections = null;
/**
* 默认构造
*/
public ConnectionPool() {
}
/**
* 自定义构造
*
* @param jdbcDriver
* 驱动
* @param dbURL
* 数据库URL
* @param username
* 用户名
* @param password
* 密码
*/
public ConnectionPool(String jdbcDriver, String dbURL, String username,
String password) {
super();
this.jdbcDriver = jdbcDriver;
this.dbURL = dbURL;
this.username = username;
this.password = password;
}
/**
* 获取数据库中测试表的名字
*
* @return testTable
*/
public String getTestTable() {
return testTable;
}
/**
* 设置测试数据表的名字
*
* @param testTable
*/
public void setTestTable(String testTable) {
this.testTable = testTable;
}
/**
* 获取连接池的初始大小
*
* @return initConnections
*/
public int getInitConnections() {
return initConnections;
}
/**
* 设置连接池的初始大小
*
* @param initConnections
*/
public void setInitConnections(int initConnections) {
this.initConnections = initConnections;
}
/**
* 获取连接池自动增加的大小
*
* @return incrementalConnections
*/
public int getIncrementalConnections() {
return incrementalConnections;
}
/**
* 设置连接池自动增加的大小
*
* @param incrementalConnections
*/
public void setIncrementalConnections(int incrementalConnections) {
this.incrementalConnections = incrementalConnections;
}
/**
* 获取连接池的最大连接数
*
* @return maxConnctions
*/
public int getMaxConnections() {
return maxConnections;
}
/**
* 设置连接池的最大连接数
*
* @param maxConnections
*/
public void setMaxConnections(int maxConnections) {
this.maxConnections = maxConnections;
}
/**
* 使程序等待mSecond毫秒
*
* @param mSecond
*/
private void wait(int mSecond) {
try {
Thread.sleep(mSecond);
} catch (InterruptedException e) {
}
}
/**
* 创建一个新的数据库连接并返回
* @return conn
* @throws SQLException
*/
private Connection newConnection() throws SQLException{
Connection conn = DriverManager.getConnection(dbURL, username, password); // 创建一个数据库连接
// 如果是第一次创建数据库连接,则检查数据库,获取最大连接数maxConnections
// 如果连接池中连接数为0,则表示连接未被创建
if(connections.size() == 0){
DatabaseMetaData metadata = conn.getMetaData();
// DatabaseMetaData接口用于描述关于数据库的整体综合信息,可以从中取得数据库的表、视图等信息
int dbMaxConnection = metadata.getMaxConnections();
/*
获取连接到此数据库的并发连接的最大可能数
如果返回的是0,则表示数据库没有最大限制或限制未知
*/
// 如果连接池设置的最大连接数量大于数据库允许的范围,则设置最大连接数为数据库允许的最大数
if(dbMaxConnection > 0 && this.maxConnections > dbMaxConnection){
this.maxConnections = dbMaxConnection;
}
}
return conn;
}
/**
* 测试一个连接是否可用
* @param conn 需要测试的连接
* @return boolean 连接是否可用
*/
private boolean testConnection(Connection conn){
// 判断测试表是否存在
try {
if("".equals(testTable)){
// 如果默认的测试表不存在,则用setAutoCommit()方法来判读连接是否可用
conn.setAutoCommit(true);
}else{
String sql = "select count(*) from " + testTable; // 连接测试表,来测试是否连接可用
PreparedStatement ps = conn.prepareStatement(sql);
ps.executeQuery();
}
} catch (SQLException e) {
// 如果try{}里语句出现异常,则连接不可用,关闭连接
closeConnection(conn);
return false;
}
// 连接可用则返回true
return true;
}
/**
* 查找连接池中的所有连接,找到其中一个可用连接并返回
* 如果没找到,则返回null
* @return conn 可用的连接
*/
@SuppressWarnings("unchecked")
private Connection findFreeConnection(){
Connection conn = null;
PooledConnection pooledConn = null;
Iterator ite = connections.iterator();
while (ite.hasNext()) {
pooledConn = (PooledConnection) ite.next();
// 如果该对象不忙,则得到该对象的连接,并将连接设为忙
if(!pooledConn.isBusy()){
conn = pooledConn.getConn();
pooledConn.setBusy(true);
// 测试连接是否可用,如果不能,则重新创建一个连接
if(!(testConnection(conn))){
try {
conn = newConnection();
} catch (SQLException e) {
System.out.println("连接创建失败!");
return null;
}
pooledConn.setConn(conn);
}
break; // 已经找到一个可用连接,退出
}
}
return conn; // 返回找到的可用连接
}
/**
* 从连接池的数据库连接的集合中得到一个可用的连接,
* 如果没用可用的,则给连接池增加几个连接重新查找
* 如果创建后仍找不到,则返回null
* @return conn 连接池中一个可用的数据库连接
* @throws SQLException
*/
private Connection getFreeConnection() throws SQLException{
// 从连接池中获得一个可用的连接
Connection conn = findFreeConnection();
if(conn == null){
// 如果连接池中没有可用的连接,则创建一些连接到连接池中
createConnections(incrementalConnections);
// 再次从连接池中查找可用的连接
conn = findFreeConnection();
if(conn == null){
// 如果创建一下连接后,仍找不到可用的连接,则返回null
return null;
}
}
return conn;
}
/**
* 得到一个可用的数据库连接,如果没有则等待一会重新获取
* @return conn 一个可用的数据库连接
* @throws SQLException
*/
public synchronized Connection getConnection() throws SQLException{
// 确定连接池已被创建
if(connections == null){
return null; // 连接池还没创建,则返回null
}
// 取得一个可用的数据库连接
Connection conn = getFreeConnection();
while(conn == null){
// 如果没用可用的连接,则等待N秒后重试
wait(500); //等待半秒钟
conn = getFreeConnection();
}
return conn;
}
/**
* 创建count个数据库连接,并将这些连接放入连接池
* @param count
* @throws SQLException
*/
@SuppressWarnings("unchecked")
private void createConnections(int count) throws SQLException{
for(int i = 0; i < count; i++){ // 循环创建指定数目的数据库连接
// 如果连接数已达到最大,则退出
if(maxConnections > 0 && this.connections.size() >= maxConnections){
break;
}
// 增加一个新的数据库连接到连接池
try {
connections.add(new PooledConnection(newConnection()));
} catch (SQLException e) {
System.out.println("数据库连接创建失败:"+e.getMessage());
throw new SQLException(); // 抛出异常到调用该私有方法的位置
}
}
System.out.println("连接创建成功!");
}
/**
* 创建一个数据库连接池,连接池中可用连接的数量有initConnections决定
* @throws InstantiationException
* @throws IllegalAccessException
* @throws ClassNotFoundException
* @throws SQLException
*/
@SuppressWarnings("unchecked")
public synchronized void createPool() throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException{
// 确定连接池没被创建
if(connections != null){
return; // 如果已创建,则返回
}
// 加载驱动
Driver driver = (Driver) (Class.forName(this.jdbcDriver)).newInstance();
DriverManager.registerDriver(driver);
connections = new ArrayList(); // 创建保存数据库连接的集合
// 根据initConnections的值,创建相应数目的连接
createConnections(initConnections);
System.out.println("数据库连接池创建成功!");
}
/**
* 刷新连接池中所有的连接对象
* @throws SQLException
*/
@SuppressWarnings( { "unchecked" })
public synchronized void refreshConnectins() throws SQLException {
// 确定连接池已经存在
if (connections == null) {
System.out.println("连接池不存在,无法刷新!");
return;
}
/*
* PooledConnection不是javax.sql中的PooledConnection Interface,而是下面写的内部类的实例化
*/
PooledConnection pooledConn = null;
Iterator ite = connections.iterator();
while (ite.hasNext()) {
pooledConn = (PooledConnection) ite.next(); // 获得一个连接对象
// 判断pooledConn是否忙,忙则等待5秒,5秒后直接刷新
if(pooledConn.isBusy()){
wait(5000);
}
// 关闭该连接,并用一个新的连接来代替
closeConnection(pooledConn.getConn());
pooledConn.setConn(newConnection()); // 创建一个新的连接
pooledConn.setBusy(false); // 设置为该连接池可用
}
}
/**
* 该方法返回一个连接到连接池中,并将该连接设置为空闲状态
* 所有使用连接池获得的数据库连接均应在不使用此连接时返回它
* @param conn
*/
@SuppressWarnings("unchecked")
public void returnConnection(Connection conn){
// 确定连接池存在,如果不存在则直接返回
if(connections == null){
System.out.println("连接池不存在,无法将该连接返回到连接池!");
return;
}
PooledConnection pooledConn = null;
Iterator ite = connections.iterator(); // 得到connections集合的迭代,以便遍历
while (ite.hasNext()) {
pooledConn = (PooledConnection) ite.next();
// 遍历集合,得到要返回的连接
if(conn == pooledConn.getConn()){ // 得到后将该连接设置为空闲状态
pooledConn.setBusy(false);
break;
}
}
}
/**
* 关闭一个数据库连接(private)
* @param conn
*/
private void closeConnection(Connection conn){
try {
conn.close();
} catch (SQLException e) {
System.out.println("关闭数据库连接错误:"+e.getMessage());
}
}
/**
* 关闭数据库连接池中的所有连接,并清空连接池
*/
@SuppressWarnings("unchecked")
public synchronized void closeConnectionPool(){
//关闭前,确定连接池是存在的,否则返回
if(connections == null){
System.out.println("连接池不存在,无法关闭!");
return;
}
PooledConnection pooledConn = null;
Iterator ite = connections.iterator();
while (ite.hasNext()) {
pooledConn = (PooledConnection) ite.next(); // 获得一个数据库连接对象
// 判断pooledConn是否忙,忙则等待5秒,5秒后直接关闭
if(pooledConn.isBusy()){
wait(5000);
}
// 5秒后直接关闭
closeConnection(pooledConn.getConn());
// 从连接池中删除该连接
connections.remove(pooledConn);
// 清空连接池
connections = null;
}
}
/*
Java synchronized 解析
一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,
一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,
另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,
其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,
它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
五、以上规则对其它对象锁同样适用
*/
// ################################################################################
// # 内部类 PooledConnection #
// ################################################################################
/**
* 用于保存连接池中连接对象的类
*
* @author FL-soft
*
*/
public class PooledConnection {
private Connection conn = null; // 数据库连接
boolean busy = false; // 判断该连接是否正在被使用,默认没有正在使用
/**
* 构造方法 传入一个Connection对象从而得到一个PooledConnection对象
*
* @param conn
*/
public PooledConnection(Connection conn) {
this.conn = conn;
}
public Connection getConn() {
return conn;
}
public void setConn(Connection conn) {
this.conn = conn;
}
/**
* 获得Connection对象的状态,是否忙
*
* @return busy
*/
public boolean isBusy() {
return busy;
}
/**
* 设置Connection对象的状态,是否正在被使用
* @param busy
*/
public void setBusy(boolean busy) {
this.busy = busy;
}
}
// ###############################################
// #测试main()方法 #
// ###############################################
public static void main(String[] args) {
ConnectionPool pool = new ConnectionPool("com.mysql.jdbc.Driver", "jdbc:mysql://localhost/downjoy", "root", "root");
try {
pool.createPool();
Connection conn = pool.getConnection();
if(conn != null){
System.out.println("OK了,数据库连接池测试成功!");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}