在服务器端涉及数据库访问的应用程序里头,数据库连接池是一项确保性能的关键技术。一谈起java数据库连接池,大家都可以罗列出一堆开源实现。
它们也各有优劣:
1. DBCP TOMCAT自带的
2. proxool
3. c3p0: see: http://sourceforge.net/projects/c3p0
4. DBPool
5. Taobao的druid
这些都是蛮不错的连接池实现。基于不用重复造轮子一说,完全可以挑一个自己喜欢的去用。已有的轮子如果没出问题,还好说,出了问题,往往很难解决。
本文的主旨是阐述连接池的基本思想:
1. 能将池化的资源(连接)控制在一定范围以内[min, max]
2. 能在jvm退出之前,释放这些连接资源
3. 能尽量简化用户的代码
4. 能确保连接池的连接始终是活跃的
下面还是上代码吧, 以下的代码是整理而成,缺点还是不少,但是也足够一般情况下的使用,至少是可控的。
1. 一个hook类,用于jvm退出前释放资源
package com.hisql;
import java.sql.SQLException;
import org.apache.log4j.Logger;
public class ReleasePoolHook implements Runnable
{
public static final Logger logger = Logger.getLogger(ReleasePoolHook.class);
ConnectionPool connpool;
public ReleasePoolHook(ConnectionPool pool)
{
// register it
connpool = pool;
Runtime.getRuntime().addShutdownHook(new Thread(this));
logger.info(">>> shutdown hook registered...");
}
@Override
public void run()
{
// TODO Auto-generated method stub
logger.info("\n>>> About to execute: " + ReleasePoolHook.class.getName() + ".run() to clean up before JVM exits.");
this.cleanUp();
logger.info(">>> Finished execution: " + ReleasePoolHook.class.getName() + ".run()");
}
private void cleanUp()
{
if (connpool != null)
{
try
{
connpool.closeConnectionPool();
logger.info("Pool realeased....");
}
catch (SQLException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
logger.warn("Pool released with exception", e);
}
}
}
}
2.PooledConnection类, 用于对Connection接口类的一个封装
很粗糙,标准的做法,应该是将Connection所有的方法都delegate,这样,用户用起来就蛮方便。这里只是为了阐述思想,如下:
package com.hisql;
import java.sql.Connection;
public class PooledConnection
{
Connection connection = null;
boolean busy = false;
public PooledConnection(Connection connection)
{
this.connection = connection;
}
public Connection getConnection()
{
return connection;
}
public void setConnection(Connection connection)
{
this.connection = connection;
}
public boolean isBusy()
{
return busy;
}
public void setBusy(boolean busy)
{
this.busy = busy;
}
}
3. ConnectionPool类
这是主体实现类:
package com.hisql;
import java.sql.*;
import java.util.*;
import org.apache.log4j.Logger;
public class ConnectionPool
{
private String jdbcDriver;
private String dbUrl;
private String dbUsername;
private String dbPassword;
private String pingSql = "select 1"; // the test sql statement to ping the target database
private int minConnections = 5;
private int incrementalConnections = 2;
private int maxConnections = 20;
private Vector<PooledConnection> connections;
private ReleasePoolHook hook;
public static final Logger logger = Logger.getLogger(ConnectionPool.class);
public ConnectionPool(String driver, String url, String username, String password)
{
jdbcDriver = driver;
dbUrl = url;
dbUsername = username;
dbPassword = password;
hook = new ReleasePoolHook(this);
}
public int getInitialConnections()
{
return this.minConnections;
}
public void setInitialConnections(int initialConnections)
{
this.minConnections = initialConnections;
}
public int getIncrementalConnections()
{
return this.incrementalConnections;
}
public void setIncrementalConnections(int incrementalConnections)
{
this.incrementalConnections = incrementalConnections;
}
public int getMaxConnections()
{
return this.maxConnections;
}
public void setMaxConnections(int maxConnections)
{
this.maxConnections = maxConnections;
}
public String getPingSql()
{
return this.pingSql;
}
public void setPingSql(String sql)
{
this.pingSql = sql;
}
/**
* intialize the pool
* @throws Exception
*/
public synchronized void initialize() throws Exception
{
if (connections != null)
{
return;
}
Class.forName(this.jdbcDriver);
connections = new Vector();
createConnections(this.minConnections);
}
private void createConnections(int numConnections) throws SQLException
{
for (int i=0; i<numConnections; i++)
{
if (this.maxConnections > 0 && this.connections.size() >= this.maxConnections)
{
break;
}
// add a new PooledConnection object
try
{
connections.addElement(new PooledConnection(newConnection()));
}
catch (SQLException e)
{
logger.error("create connection failed: ", e);
throw new SQLException();
}
logger.info(" connection created ......");
}
}
private Connection newConnection() throws SQLException
{
Connection conn = DriverManager.getConnection(dbUrl, dbUsername, dbPassword);
if (connections.size() == 0)
{
DatabaseMetaData metaData = conn.getMetaData();
int driverMaxConnections = metaData.getMaxConnections();
if (driverMaxConnections > 0 && this.maxConnections > driverMaxConnections)
{
this.maxConnections = driverMaxConnections;
}
}
return conn;
}
public synchronized Connection getConnection() throws SQLException
{
if (connections == null)
{
return null;
}
Connection conn = getFreeConnection();
while (conn == null)
{
wait(250);
conn = getFreeConnection();
}
return conn;
}
private Connection getFreeConnection() throws SQLException
{
Connection conn = findFreeConnection();
if (conn == null)
{
createConnections(incrementalConnections);
conn = findFreeConnection();
if (conn == null)
{
return null;
}
}
return conn;
}
private Connection findFreeConnection() throws SQLException
{
Connection conn = null;
PooledConnection pConn = null;
Iterator<PooledConnection> iter = connections.iterator();
while (iter.hasNext())
{
pConn = (PooledConnection)iter.next();
if (!pConn.isBusy())
{
conn = pConn.getConnection();
pConn.setBusy(true);
if (!testConnection(conn))
{
try
{
conn = newConnection();
}
catch(SQLException e)
{
logger.error("create connection failed:", e);
return null;
}
pConn.setConnection(conn);
}
break;
}
}
return conn;
}
private boolean testConnection(Connection conn)
{
Statement stmt = null;
ResultSet rset = null;
try
{
stmt = conn.createStatement();
rset = stmt.executeQuery(this.pingSql);
}
catch (SQLException ex)
{
closeConnection(conn);
return false;
}
finally
{
try
{
if (rset!= null) rset.close();
}
catch (SQLException ex) {}
try
{
if (stmt!= null) stmt.close();
}
catch (SQLException ex) {}
}
return true;
}
public void returnConnection(Connection conn)
{
if (connections == null)
{
logger.warn("connection pool not exists.");
return;
}
PooledConnection pConn = null;
Enumeration enumerate = connections.elements();
while (enumerate.hasMoreElements())
{
pConn = (PooledConnection)enumerate.nextElement();
if (conn == pConn.getConnection())
{
pConn.setBusy(false);
break;
}
}
}
public synchronized void refreshConnections() throws SQLException
{
if (connections == null)
{
logger.warn("connection pool not exists, can't refresh...");
return;
}
PooledConnection pConn = null;
Enumeration enumerate = connections.elements();
while (enumerate.hasMoreElements())
{
pConn = (PooledConnection)enumerate.nextElement();
if (pConn.isBusy())
{
wait(5000);
}
closeConnection(pConn.getConnection());
pConn.setConnection(newConnection());
pConn.setBusy(false);
}
}
public synchronized void closeConnectionPool() throws SQLException
{
if (connections == null)
{
logger.warn("conneciton pool not exists, can't close..");
return;
}
PooledConnection pConn = null;
Enumeration enumerate = connections.elements();
while (enumerate.hasMoreElements())
{
pConn = (PooledConnection)enumerate.nextElement();
if (pConn.isBusy())
{
wait(5000);
}
closeConnection(pConn.getConnection());
connections.removeElement(pConn);
}
connections = null;
}
private void closeConnection(Connection conn)
{
try
{
conn.close();
}
catch (SQLException ex)
{
logger.warn("close connection error: ", ex);
}
}
private void wait(int mSeconds)
{
try
{
Thread.sleep(mSeconds);
}
catch (InterruptedException e)
{
}
}
}
使用方法:
ConnectionPool connpool = new ConnectionPool("com.mysql.jdbc.Driver", "jdbc:mysql://localhost:3306/foo", "root", "******");
connpool.initialize();
Connection conn = connpool.getConnection();
try
{
......
}
catch ()
finally
{
connpool.returnConnection(conn);
}
三个类只依赖于log4j-1.2.*.jar 和 commons-logging-*.jar,所以很容易构建一个工程。
改进思路:
1. 对PooledConnection类,增加相应的delegate方法,让用户看不到connpool.returnConnection()这类api。只需要关注connection.close(),不是真正的close()
2. 对pingConnection操作,提供一个工作线程,使其能确保连接池中所有的connection都是活连接。现实中,有些数据库如果空闲时间超时,比如1个小时,它是自动断开客户端连接的,这样会对应用产生负责影响。
3. 最好能自动收缩,如果连接长期不使用,可以进行物理释放,让真正打开的连接处于[min, max]之间。而不是达到max以后,物理连接就始终占用max个资源,这不尽合理。
分享到:
相关推荐
总结,Java 完整的数据库连接池实现涉及到选择合适的连接池实现、配置参数、初始化连接池以及在代码中正确地获取和释放连接。理解这些概念和步骤对于优化 Java 应用程序的数据库性能至关重要。通过合理配置和使用...
Java JDBC 数据库连接池总结 Java 语言中,JDBC(Java DataBase Connection)是应用程序与数据库沟通的桥梁。在 Web 应用开发的早期,主要使用的技术是 CGIASPPHP 等。之后,Sun 公司推出了基于 Java 语言的 ...
标题:Java数据库连接池 描述:本篇将详细介绍Java数据库连接池的概念,代码实现以及其在实际项目中的应用。数据库连接池是Java开发中优化数据库访问效率的关键技术,通过复用预创建的数据库连接,避免频繁创建和...
Java数据库连接池是Java开发中一个非常重要的技术,它主要用于管理数据库连接,提高数据库操作的效率和性能。在Java应用程序中,频繁地创建和关闭数据库连接会导致大量的系统资源浪费,而连接池则可以复用已建立的...
本篇文章将深入解析一个自定义的JAVA数据库连接池类,帮助开发者更好地理解和运用这一关键技术。 首先,连接池的基本思想是预先创建一定数量的数据库连接,并存储在一个集合(如Vector)中,供应用程序按需获取和...
### JAVA 使用数据库连接池连接Oracle数据库全代码解析 #### 一、概述 本文将详细介绍如何在Java项目中使用Apache DBCP(Database Connection Pool)来连接Oracle数据库,并提供完整的示例代码。通过这种方式,我们...
本项目提供了一个轻量级的Java数据库连接池实现,旨在简化开发过程,提高应用性能并降低资源消耗。下面将详细介绍这个轻量级Java数据库连接池的设计理念、工作原理以及如何使用。 1. **设计理念** 轻量级数据库...
总的来说,Java数据库连接池如C3P0和Druid,以及Spring的JdbcTemplate,都是为了优化数据库操作而设计的工具。它们各自有其特点和优势,开发者可以根据项目需求选择合适的技术。在使用过程中,充分理解和合理配置...
本文将深入探讨如何使用Java代码来实现一个简单的数据库连接池,并解释其核心概念和工作原理。 连接池的基本思想是维护一组预初始化的数据库连接,当应用程序需要时,可以从池中获取一个连接,使用完毕后,再归还回...
Java数据库连接池的原理与应用 在Java开发领域,数据库连接池(Database Connection Pool,简称DBCP)是一种提高数据库访问性能、降低资源消耗的重要技术。它通过预先建立一定数量的数据库连接,存储在一个“池”中...
2. **常见的Java数据库连接池实现** - C3P0:一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。 - DBCP (Apache BasicDataSource):Apache的一个开源项目,基于Jakarta Pool实现...
Java JDBC 数据库连接池是 Java 应用程序访问数据库的基本原理之一。Java 语言通过 JDBC 技术访问数据库,JDBC 是一种“开放”的方案,为数据库应用开发人员和数据库前台工具开发人员提供了一种标准的应用程序设计...