编写自己的jdbc数据库连接池

1、为什么要使用数据库连接池

在使用jdbc的一般开发中,每次都要从数据库获取连接,典例的查询的做法如下:

		Connection conn = null;
		PreparedStatement st = null;
		ResultSet rs = null;
		try{
			Class.forName("com.mysql.jdbc.Driver");
			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "123456");
			String sql = "SELECT * FROM persons";
			st = conn.prepareStatement(sql);
			rs = st.executeQuery();
			while(rs.next()){
				//在这里将查询到的数据封装或者做一些其他的事情
				//.....
			}
		}catch(Exception e){
			throw new RuntimeException(e);
		}finally{//close resource
			if(rs != null){
				try {
					rs.close();
				} catch (Exception e) {
					e.printStackTrace();
				}finally{
					rs = null;
				}
			}
			if(st != null){
				try {
					st.close();
				} catch (Exception e) {
					e.printStackTrace();
				}finally{
					st = null;
				}
			}
			if(conn != null){
				try {
					conn.close();
				} catch (Exception e) {
					e.printStackTrace();
				}finally{
					conn = null;
				}
			}
		}

这样写一次两次次还好,如果存在几百个这样的方法,那真的是一件痛苦的事情。每次都要去创建连接,不仅难以管理,而且每次创建连接都需要较长的时间,降低了性能。有没有可能将这些链接统一的管理,每次获取链接时不再自己手动的去获取连接,而是直接的去缓存里面拿连接呢?

2、开始编写自己的数据库连接池

首先编写一些用于获取连接的工具类:

<pre name="code" class="java">package cn.zq.util;

import java.sql.Connection;
import java.sql.DriverManager;

public class JdbcUtil {
	private static Connection conn;

	static{
		try {
			/* jdbc4.0规范:
			 * 如果在jdbc驱动的jar包的
			 *   META-INF/services/java.sql.Driver文件中写入相应的驱动的类名,
			 *  则Class.forName("com.mysql.jdbc.driver")就可以不用写了,
			 *  不过为了保险起见,建议这句话最好还是写上
			 */
			Class.forName("com.mysql.jdbc.Driver");
			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "123456");
		} catch (Exception e) {
			 throw new ExceptionInInitializerError(e);
		}
	}

	public static Connection getConnection(){
		return conn;
	}
}

每次获取连接时,直接用JdbcUtil.getConnection()去获取连接,相比前面的直接去获取连接要简单点,这个工具类只维护一个Connection,而且每次用完连接还不能关闭该连接,要不然下次再去获取连接再做一些其他的事情时就会抛出com.mysql.jdbc.exceptions.MySQLNonTransientConnectionException: No operations allowed after connection closed异常。多个线程用这个连接,这几个线程的事务也会存在问题。仅仅就这个连接根本就满足不了要求,那么就需要用一个容器来缓存多个连接,改造后:

package cn.zq.util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.util.ArrayList;

public class JdbcUtil {
	private static ArrayList<Connection> pool = new ArrayList<Connection>();

	static{
		try {
			/* jdbc4.0规范:
			 * 如果在jdbc驱动的jar包的
			 *   META-INF/services/java.sql.Driver文件中写入相应的驱动的类名,
			 *  则Class.forName("com.mysql.jdbc.driver")就可以不用写了,
			 *  不过为了保险起见,建议这句话最好还是写上
			 */
			Class.forName("com.mysql.jdbc.Driver");

			String url = "jdbc:mysql://localhost:3306/test";
			String user = "root";
			String pwd = "123456";
			int initialSize = 3;
			for(int i = 0; i < initialSize; i++){
				Connection conn = DriverManager.getConnection(url, user, pwd);
				pool.add(conn);
			}
		} catch (Exception e) {
			throw new ExceptionInInitializerError(e);
		}
	}

	public static  synchronized Connection getConnection(){
		return pool.remove(0);
	}
}

这样初始化时就可以获取3个连接,这样做还有一个问题:只能从里面拿3个连接,如果再拿第4个连接时就会抛出异常,还需要改造。应该提供一个方法,让程序员手动的还连接

package cn.zq.util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.util.ArrayList;

public class JdbcUtil2 {
	private static ArrayList<Connection> pool = new ArrayList<Connection>();

	static{
		try {
			/* jdbc4.0规范:
			 * 如果在jdbc驱动的jar包的
			 *   META-INF/services/java.sql.Driver文件中写入相应的驱动的类名,
			 *  则Class.forName("com.mysql.jdbc.driver")就可以不用写了,
			 *  不过为了保险起见,建议这句话最好还是写上
			 */
			Class.forName("com.mysql.jdbc.Driver");

			String url = "jdbc:mysql://localhost:3306/test";
			String user = "root";
			String pwd = "123456";
			int initialSize = 3;
			for(int i = 0; i < initialSize; i++){
				Connection conn = DriverManager.getConnection(url, user, pwd);
				pool.add(conn);
			}
		} catch (Exception e) {
			throw new ExceptionInInitializerError(e);
		}
	}

	public static  synchronized Connection getConnection(){
		return pool.remove(0);
	}

	public static synchronized void close(Connection conn){
		if(conn != null){
			pool.add(conn);
		}
	}
}

虽然这样能解决问题,但是程序员不一定会安装要求去调用这个方法,一般喜欢调用Connection.close()方法,能不能让程序员调用这个方法时就还连接呢?那么就需要对Connection的close方法进行增强,一般有这几个方式:继承、包装、代理。下面就演示下用代理来增强:

package cn.zq.util;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.LinkedList;

/**
 * @author zq
 *
 */
public class JdbcUtil2 {

	//Connection pool
	private static LinkedList<Connection> pool = new LinkedList<Connection>();

	static{
		try {
			String driver = "com.mysql.jdbc.Driver";
			String url = "jdbc:mysql:///test";
			String user = "root";
			String pwd = "123456";
			int initialSize = 3;

			Class.forName(driver);
			for(int i = 0; i < initialSize; i++){
				final Connection conn = DriverManager.getConnection(url, user, pwd);

				//proxy
				Object connProxy = Proxy.newProxyInstance(JdbcUtil.class.getClassLoader(),
									new Class[]{Connection.class},
									new InvocationHandler() {

										public Object invoke(Object proxy, Method method, Object[] args)
												throws Throwable {

											//只对close方法进行增强
											if(method.getName().equals("close")){
												synchronized (pool) {
													pool.addLast((Connection) proxy);
													pool.notify();
													return null;
												}
											}

											return method.invoke(conn, args);
										}
									});

				pool.addLast((Connection) connProxy);
			}
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage(), e);
		}
	}

	public static Connection getConnection(){
		synchronized (pool) {
			if(pool.size() == 0){
				try {
					pool.wait();
				} catch (InterruptedException e) {
					throw new RuntimeException(e.getMessage(), e);
				}
			}
			Connection conn = pool.removeFirst();;
			return conn;
		}
	}

	public static void release(ResultSet rs, Statement st, Connection conn){
		if(rs != null){
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}finally{
				rs = null;
			}
		}

		if(conn != null){
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}finally{
				conn = null;
			}
		}

		if(st != null){
			try {
				st.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}finally{
				st = null;
			}
		}

	}
}

上面的代码还有可以优化的地方,比如驱动,用户名、密码等等都应该从一个配置文件里面读取,还有上面的连接在类加载的时候进行放到连接池中的,那么为什么不在获取连接的时候进行代理呢?那么就可以减少内存的消耗。改造后如下:

package cn.zq.util;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.LinkedList;
import java.util.Properties;

/**
 * @author zq
 *
 */
public class JdbcUtil {

	//Connection pool
	private static LinkedList<Connection> pool = new LinkedList<Connection>();

	static{
		try {
			Properties prop = loadJdbcConfig("jdbc.properties");
			String driver = prop.getProperty("driver");
			String url = prop.getProperty("url");
			String user = prop.getProperty("user");
			String pwd = prop.getProperty("pwd");
			int poolSize = new Integer(prop.getProperty("initialSize"));

			Class.forName(driver);
			for(int i = 0; i < poolSize; i++){
				Connection conn = DriverManager.getConnection(url, user, pwd);
				pool.addLast((Connection) conn);
			}
		} catch (Exception e) {
			throw new ExceptionInInitializerError(e);
		}
	}

	private static Properties loadJdbcConfig(String filename) throws IOException{
		InputStream in = null;
		try {
			in = JdbcUtil.class
								.getClassLoader()
								.getResourceAsStream(filename);
			Properties prop = new Properties();
			prop.load(in);
			return prop;
		}finally{
			if(in != null){
				in.close();
			}
		}
	}

	public static Connection getConnection(){
		synchronized (pool) {
			if(pool.size() == 0){
				try {
					pool.wait();
				} catch (InterruptedException e) {
					throw new RuntimeException(e.getMessage(), e);
				}
			}
			final Connection conn = pool.removeFirst();;
			//proxy
			Object connProxy = Proxy.newProxyInstance(JdbcUtil.class.getClassLoader(),
								new Class[]{Connection.class},
								new InvocationHandler() {

									public Object invoke(Object proxy, Method method, Object[] args)
											throws Throwable {
										if(method.getName().equals("close")){
											synchronized (pool) {
												pool.addLast(conn);
												pool.notify();
												return null;
											}
										}
										return method.invoke(conn, args);
									}
								});
			return (Connection) connProxy;
		}
	}

	public static void release(ResultSet rs, Statement st, Connection conn){
		if(rs != null){
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}finally{
				rs = null;
			}
		}

		if(conn != null){
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}finally{
				conn = null;
			}
		}

		if(st != null){
			try {
				st.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}finally{
				st = null;
			}
		}

	}
}

jdbc.properties

driver = com.mysql.jdbc.Driver
url = jdbc:mysql:///contacts?characterEncoding=UTF8
username = root
pwd =123456
initialSize=2

使用包装来完成上面的事情:

package cn.zq.util;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.LinkedList;
import java.util.Map;
import java.util.Properties;

/**
 * @author zq
 *
 */
public class JdbcUtil3 {

	//Connection pool
	private static LinkedList<Connection> pool = new LinkedList<Connection>();

	static{
		try {
			Properties prop = loadJdbcConfig("jdbc.properties");
			String driver = prop.getProperty("driver");
			String url = prop.getProperty("url");
			String user = prop.getProperty("user");
			String pwd = prop.getProperty("pwd");
			int poolSize = new Integer(prop.getProperty("initialSize"));

			Class.forName(driver);
			for(int i = 0; i < poolSize; i++){
				Connection conn = DriverManager.getConnection(url, user, pwd);
				pool.addLast((Connection) conn);
			}
		} catch (Exception e) {
			throw new ExceptionInInitializerError(e);
		}
	}

	private static Properties loadJdbcConfig(String filename) throws IOException{
		InputStream in = null;
		try {
			in = JdbcUtil3.class
								.getClassLoader()
								.getResourceAsStream(filename);
			Properties prop = new Properties();
			prop.load(in);
			return prop;
		}finally{
			if(in != null){
				in.close();
			}
		}
	}

	public static Connection getConnection(){
		synchronized (pool) {
			if(pool.size() == 0){
				try {
					pool.wait();
				} catch (InterruptedException e) {
					throw new RuntimeException(e.getMessage(), e);
				}
			}
			Connection conn = pool.removeFirst();;

			return new ConnectionWrapper(conn);
		}
	}

	public static void release(ResultSet rs, Statement st, Connection conn){
		if(rs != null){
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}finally{
				rs = null;
			}
		}

		if(conn != null){
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}finally{
				conn = null;
			}
		}

		if(st != null){
			try {
				st.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}finally{
				st = null;
			}
		}

	}

  static class ConnectionWrapper implements Connection {

		private Connection conn;

		public void close() throws SQLException {
			synchronized (pool) {
				pool.addLast(conn);
				pool.notify();
			}
		}

		public ConnectionWrapper(Connection conn) {
			this.conn = conn;
		}
		public <T> T unwrap(Class<T> iface) throws SQLException {
			return conn.unwrap(iface);
		}

		public boolean isWrapperFor(Class<?> iface) throws SQLException {
			return conn.isWrapperFor(iface);
		}

		public Statement createStatement() throws SQLException {
			return conn.createStatement();
		}

		public PreparedStatement prepareStatement(String sql)
				throws SQLException {
			return conn.prepareStatement(sql);
		}

		public CallableStatement prepareCall(String sql) throws SQLException {
			return conn.prepareCall(sql);
		}

		public String nativeSQL(String sql) throws SQLException {
			return conn.nativeSQL(sql);
		}

		public void setAutoCommit(boolean autoCommit) throws SQLException {
			conn.setAutoCommit(autoCommit);
		}

		public boolean getAutoCommit() throws SQLException {
			return conn.getAutoCommit();
		}

		public void commit() throws SQLException {
			conn.commit();
		}

		public void rollback() throws SQLException {
			conn.rollback();
		}

		public boolean isClosed() throws SQLException {
			return conn.isClosed();
		}

		public DatabaseMetaData getMetaData() throws SQLException {
			return conn.getMetaData();
		}

		public void setReadOnly(boolean readOnly) throws SQLException {
			conn.setReadOnly(readOnly);
		}

		public boolean isReadOnly() throws SQLException {
			return conn.isReadOnly();
		}

		public void setCatalog(String catalog) throws SQLException {
			conn.setCatalog(catalog);
		}

		public String getCatalog() throws SQLException {
			return conn.getCatalog();
		}

		public void setTransactionIsolation(int level) throws SQLException {
			conn.setTransactionIsolation(level);
		}

		public int getTransactionIsolation() throws SQLException {
			return conn.getTransactionIsolation();
		}

		public SQLWarning getWarnings() throws SQLException {
			return conn.getWarnings();
		}

		public void clearWarnings() throws SQLException {
			conn.clearWarnings();

		}

		public Statement createStatement(int resultSetType,
				int resultSetConcurrency) throws SQLException {
			return conn.createStatement(resultSetType, resultSetConcurrency);
		}

		public PreparedStatement prepareStatement(String sql,
				int resultSetType, int resultSetConcurrency)
				throws SQLException {
			return conn.prepareStatement(sql, resultSetConcurrency);
		}

		public CallableStatement prepareCall(String sql, int resultSetType,
				int resultSetConcurrency) throws SQLException {
			return conn.prepareCall(sql, resultSetType, resultSetConcurrency);
		}

		public Map<String, Class<?>> getTypeMap() throws SQLException {
			return conn.getTypeMap();
		}

		public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
			conn.setTypeMap(map);
		}

		public void setHoldability(int holdability) throws SQLException {
			conn.setHoldability(holdability);
		}

		public int getHoldability() throws SQLException {
			return conn.getHoldability();
		}

		public Savepoint setSavepoint() throws SQLException {
			return conn.setSavepoint();
		}

		public Savepoint setSavepoint(String name) throws SQLException {
			return conn.setSavepoint(name);
		}

		public void rollback(Savepoint savepoint) throws SQLException {
			conn.rollback(savepoint);
		}

		public void releaseSavepoint(Savepoint savepoint) throws SQLException {
			conn.releaseSavepoint(savepoint);
		}

		public Statement createStatement(int resultSetType,
				int resultSetConcurrency, int resultSetHoldability)
				throws SQLException {
			return conn.createStatement(resultSetType, resultSetConcurrency,
					resultSetHoldability);
		}

		public PreparedStatement prepareStatement(String sql,
				int resultSetType, int resultSetConcurrency,
				int resultSetHoldability) throws SQLException {
			return conn.prepareStatement(sql, resultSetType,
					resultSetConcurrency, resultSetHoldability);
		}

		public CallableStatement prepareCall(String sql, int resultSetType,
				int resultSetConcurrency, int resultSetHoldability)
				throws SQLException {
			return conn.prepareCall(sql, resultSetType, resultSetConcurrency,
					resultSetHoldability);
		}

		public PreparedStatement prepareStatement(String sql,
				int autoGeneratedKeys) throws SQLException {
			return conn.prepareStatement(sql, autoGeneratedKeys);
		}

		public PreparedStatement prepareStatement(String sql,
				int[] columnIndexes) throws SQLException {

			return conn.prepareStatement(sql, columnIndexes);
		}

		public PreparedStatement prepareStatement(String sql,
				String[] columnNames) throws SQLException {
			return conn.prepareStatement(sql, columnNames);
		}

		public Clob createClob() throws SQLException {
			return conn.createClob();
		}

		public Blob createBlob() throws SQLException {
			return conn.createBlob();
		}

		public NClob createNClob() throws SQLException {
			return conn.createNClob();
		}

		public SQLXML createSQLXML() throws SQLException {
			return conn.createSQLXML();
		}

		public boolean isValid(int timeout) throws SQLException {
			return conn.isValid(timeout);
		}

		public void setClientInfo(String name, String value)
				throws SQLClientInfoException {
			conn.setClientInfo(name, value);
		}

		public void setClientInfo(Properties properties)
				throws SQLClientInfoException {
			conn.setClientInfo(properties);
		}

		public String getClientInfo(String name) throws SQLException {
			return conn.getClientInfo(name);
		}

		public Properties getClientInfo() throws SQLException {
			return conn.getClientInfo();
		}

		public Array createArrayOf(String typeName, Object[] elements)
				throws SQLException {
			return conn.createArrayOf(typeName, elements);
		}

		public Struct createStruct(String typeName, Object[] attributes)
				throws SQLException {
			return conn.createStruct(typeName, attributes);
		}
	}

}

java提供了标准的数据库连接池的接口javax.sql.DataSource,定义了数据库连接池的一些方法,读者可以自行完成标准的数据库连接池。

时间: 2024-10-21 15:05:45

编写自己的jdbc数据库连接池的相关文章

在JAVA中实现JDBC数据库连接池

[转自e良师益友网]Java程序员都很羡慕Windows ADO ,只需要new Connection 就可以直接从数据库连接池中返回Connection.并且 ADO Connection 是线程安全的,多个线程可以共用一个Connection,所以ASP程序一般都把getConnection 放在 Global.asa 文件中,在 IIS 启动时建立数据库连接.ADO 的Connection 和Result 都有很好的缓冲,并且很容易使用.推荐学习尚硅谷JDBC视频教程. 其实我们可以自己写

Java之中JDBC数据库连接池实现方法

作为一名初级Java程序员都很羡慕Windows ADO ,只需要new Connection 就可以直接从数据库连接池中返回Connection.并且 ADO Connection 是线程安全的,多个线程可以共用一个Connection,所以ASP程序一般都把getConnection 放在 Global.asa 文件中,在 IIS 启动时建立数据库连接.ADO 的Connection 和Result 都有很好的缓冲,并且很容易使用. 其实我们可以自己写一个JDBC数据库连接池. 写JDBC

【Java123】JDBC数据库连接池建立

需求场景:多SQL任务多线程并行执行 解决方案:建立JDBC数据库连接池,将线程与连接一对一绑定 https://www.cnblogs.com/panxuejun/p/5920845.html https://blog.csdn.net/sunbo94/article/details/79409298 https://www.cnblogs.com/hanfight/p/4701763.html 需求场景:单次查询的ResultSet数据量过大 解决方案:分页查询+多线程任务执行 https:

JAVA JDBC 数据库连接池

1.1 JDBC数据库连接池的必要性 在使用开发基于数据库的web程序时,传统的模式基本是按以下步骤: 在主程序(如servlet.beans)中建立数据库连接 进行sql操作 断开数据库连接 这种模式开发,存在的问题: 普通的JDBC数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将 Connection 加载到内存中,再验证用户名和密码(得花费0.05s-1s的时间).需要数据库连接的时候,就向数据库要求一个,执行完成后再断开连接.这样的方式将会消耗大量的资

JDBC 数据库连接池 小结

原文:http://www.cnblogs.com/lihuiyy/archive/2012/02/14/2351768.html 当对数据库的访问不是很频繁时,可以在每次访问数据库时建立一个连接,用完之后关闭.但是,对于一个复杂的数据库应用,频繁的建立.关闭连接,会极大的减低系统性能,造成瓶颈.所以可以使用数据库连接池来达到连接资源的共享,使得对于数据库的连接可以使高效.安全的复用. 1.通过一个小测试来了解数据库连接池的原理 (1)创建一个属性文件  dbpool.properties 1

JDBC数据库连接池技术

在JDBC中,获得连接或释放资源是非常消耗系统资源的两个过程,为了解决此类性能问题,通常采用连接池技术,来共享连接.这样我们就不需要每次都创建连接.释放连接了,这些操作都交给了连接池. 用池的概念来管理Connection,这样可以重复使用Connection.有了连接池以后就不用自己亲自创建连接而是通过连接池来获得Connection对象.当使用完Connection之后,调用Connection的close()方法不是真的将连接关闭,而是把Connection归还给连接池.连接池就可以继续保

java攻城狮之路--复习JDBC(数据库连接池 : C3P0、DBCP)

复习数据库连接池 : C3P0.DBCP 1.数据库连接池技术的优点: •资源重用: 由于数据库连接得以重用,避免了频繁创建,释放连接引起的大量性能开销.在减少系统消耗的基础上,另一方面也增加了系统运行环境的平稳性. •更快的系统反应速度: 数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于连接池中备用.此时连接的初始化工作均已完成.对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而减少了系统的响应时间. •新的资源分配手段: 对于多应用共享同

Application, JDBC, 数据库连接池, Session, 数据库的关系

RT,这几个东东已经困扰我很长一段时间了... 这次争取把她们理清楚了! 参考资料: 1. 数据库连接池:http://www.cnblogs.com/shipengzhi/archive/2011/06/07/2074396.html http://pengshao.iteye.com/blog/1672717

JDBC数据库连接池

声明:本栏目所使用的素材都是凯哥学堂VIP学员所写,学员有权匿名,对文章有最终解释权:凯哥学堂旨在促进VIP学员互相学习的基础上公开笔记. DBCP DataBase Connection Pool资源管理不是非常的好. 一般情况下DiverManager.getConnection获得连接对象 每次都这么做,十分的耗费资源. 为了解决这个问题,于是出现连接池.跟线程池的概念差不多. 线程池: 多个Connection对象被创建了,然后放在池子里面,用的时候直接从池子里面去拿,用完了再放回去.