日期:2014-05-16  浏览次数:20453 次

自定义JDBC连接池及常用连接池介绍

[coolxing按: 转载请注明作者和出处, 如有谬误, 欢迎在评论中指正.]

?

如果不采用连接池技术, 将导致不断创建和销毁数据库Connection, 造成性能上的损耗. 而数据库连接池技术将在池中创建一定数量的Connection, 当需要Connection时就从池中取出一个, 用完之后归还给连接池, 而不是将其销毁.

自定义数据库连接池的具体步骤分析:

a. 定义MyDataSource类, 实现DataSource接口, 并提供接口中定义的方法. 其中, 核心方法为Connection getConnection().

b. 可以在静态代码块或构造函数中创建多个Connection对象, 并将他们存储在LinkedList中. 当调用MyDataSource对象的getConnection()方法时, 从集合中取出一个Connection返回给调用者. 当调用者使用完Connection并调用Connection对象的close()方法时, 将该Connection对象归还给连接池.

c. 调用Connection对象的close()方法时, 为阻止系统销毁该连接, 而改为将连接归还给连接池, 可以采用包装设计模式. 此时包装的目标类为Connection, 因此需要定义MyConnection类并实现Connection接口. MyConnection类应该存在2个成员: Connection对象和LinkedList<Connection>对象, 这2个成员可以通过构造函数存入. 同时需要覆写close()方法, 在close()方法中实现将Connection添加到集合中.

代码如下:

?

public class MyDataSource implements DataSource {
	// 存储Connection对象的集合, 该集合最好采用LinkedList实现, 因为涉及到大量的删除和添加操作.
	private List<Connection> conns = new LinkedList<Connection>();

	public MyDataSource(String propertiesFileName) throws IOException,
			ClassNotFoundException, SQLException {
		// 连接池中Connection对象的数量
		int initialSize = 10;
		// 根据配置文件的名称获得InputStream对象
		InputStream in = MyDataSource.class.getClassLoader()
				.getResourceAsStream(propertiesFileName);
		// 使用Properties读取配置文件
		Properties prop = new Properties();
		prop.load(in);
		String url = prop.getProperty("url");
		String user = prop.getProperty("user");
		String password = prop.getProperty("password");
		String driver = prop.getProperty("driver");

		// 加载数据库驱动
		Class.forName(driver);
		// 根据配置文件中的信息获取数据库连接
		for (int i = 0; i < initialSize; i++) {
			Connection conn = DriverManager.getConnection(url, user, password);
			conns.add(conn);
		}
	}

	@Override
	public Connection getConnection() throws SQLException {
		// 获取从集合中移除的Connection对象, 并将其包装为MyConnection对象后返回.
		Connection conn = conns.remove(0);
		// 测试代码
		System.out.println("获取连接之后, 连接池中的Connection对象的数量为: " + conns.size());
		return new MyConnection(conn, conns);
	}

	private class MyConnection implements Connection {
		private Connection conn;
		private List<Connection> conns;

		public MyConnection(Connection conn, List<Connection> conns) {
			this.conn = conn;
			this.conns = conns;
		}

		// 重写close()方法, 实现调用该方法时将连接归还给连接池
		@Override
		public void close() throws SQLException {
			conns.add(conn);
			// 测试代码
			System.out.println("将连接归还给连接池后, 连接池中的Connection对象的数量为: " + conns.size());
		}

		// 对于其他方法, 直接调用conn对象的相应方法即可
		@Override
		public void clearWarnings() throws SQLException {
			conn.clearWarnings();
		}

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

		// 省略了其余Connection接口中定义的方法...

	}

	// 省略了其他DataSource接口中定义的方法...
}

以下是测试代码:

// create table person(id int primary key auto_increment, name varchar(40), age int);
public class JDBCPool {
	@Test
	public void myDataSourceTest() {
		Connection conn = null;
		PreparedStatement st = null;
		ResultSet rs = null;
		try {
			MyDataSource pool = new MyDataSource("db.properties");
			conn = pool.getConnection();
			String sql = "insert into person(name, age) values('min', 22)";
			st = conn.prepareStatement(sql);
			st.executeUpdate();

			sql = "select * from person";
			st = conn.prepareStatement(sql);
			rs = st.executeQuery();
			if (rs.next()) {
				System.out.println("id = " + rs.getInt("id") + ", name = "
						+ rs.getString("name") + ", age = " + rs.getInt("age"));
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 在release方法内会调用conn对象的close()方法
			release(null, st, conn);
		}
	}

	// 释放资源
	public void release(ResultSet rs, Statement st, Connection conn) {
		try {
			if (rs != null) {
				rs.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
			rs = null;
		} finally {
			try {
				if (st != null) {
					st.close();
				}
			} catch (SQLExce