编写数据库连接池的步骤:
1、要实现java.sql.DataSource接口,
2、在DataSource的构造函数里面,批量创建与数据库的连接.并把创建的数据库连接加入LinkedList. (因为用LinkedList便于增加和删除)
3、实现getConnection()方法,每次调用getConnection()方法时,在LinkedList中,去获取一个连接,并返回给用户
4、当用户用完连接Connection时,调用conn.close()方法时,应该将连接Connection返回给LinkedList,而不是返回给数据库.
5、编程的难点在于,当用户用完连接Connection时,调用conn.close()方法时,应该将连接Connection返回给LinkedList,而不是返回给数据库.利用动态代理来实现
6、自己手动实现的数据库连接池JDBCPool代码,如下
package com.jdbc.enhancegenericitypool.utils;
import java.io.PrintWriter;
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.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.LinkedList;
import java.util.Properties;
import java.util.logging.Logger;
import javax.sql.DataSource;
import com.jdbc.utils.MyJDBCUtils;
/**
* 只需要重写getConnection()方法即可
*
*/
public class JDBCPool implements DataSource {
/**
* 为了程序的更好的解耦合,利用Properties文件保存连接Mysql的配置文件
*/
private static Properties config = new Properties();
/**
* 用于保存连接池中的Connection
*/
private LinkedList<Connection> connPool = new LinkedList<Connection>();
/**
* 默认Connection初始化的数量为3个
*/
private int connCount = 3;
// 在静态代码块里面,加载并且初始化Mysql的配置
static {
try {
config.load(MyJDBCUtils.class.getClassLoader().getResourceAsStream(
"db.properties"));
Class.forName(config.getProperty("driver"));
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
/**
* 1、在JDBCPool的构造方法中,初始化连接池的数量
*/
public JDBCPool() {
for (int i = 0; i < connCount; i++) {
connPool.add(getConnectionToConnPool());
}
}
/**
* 获取一个数据库的连接,并加入到数据库连接池connPool中
*
* @return
*/
private Connection getConnectionToConnPool() {
Connection conn = null;
try {
conn = DriverManager.getConnection(config.getProperty("url"),
config.getProperty("username"),
config.getProperty("password"));
} catch (SQLException e) {
throw new RuntimeException("获取连接Mysql数据库连接失败");
}
return conn;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public Connection getConnection() throws SQLException {
if (connPool.size() <= 0) {
throw new RuntimeException("服务器繁忙");
}
// 获取数据库的连接池里面的Connection
Connection conn = connPool.removeFirst();
// 使用动态代理的方式,来加强Connection的close()方法,
// 是的连接的释放,会回到conns中,而不是数据库连接池中
Connection proxyConn = (Connection) Proxy.newProxyInstance(this
.getClass().getClassLoader(), conn.getClass().getInterfaces(),
new MyInvocationHabdle(conn) {
});
return proxyConn;
}
@Override
public Connection getConnection(String username, String password)
throws SQLException {
return null;
}
// 创建内部类MyInvocationHabdle,用于动态代理
public class MyInvocationHabdle implements InvocationHandler {
/**
* 保存被代理的对象
*/
private Connection conn;
public MyInvocationHabdle(Connection conn) {
this.conn = conn;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 当调用close方法时,把数据库的连接返回给conns,
if ("close".equals(method.getName())) {
connPool.add(conn);
return null;
}
// 调用其他方法时,直接返回
return method.invoke(conn, args);
}
}
}
7、对于连接池的测试代码,证明获取的数据库的连接就是连接池里面的
package com.jdbc.enhancegenericitypool.utils;
import java.sql.Connection;
import java.sql.SQLException;
public class PoolTest {
public static void main(String[] args) throws SQLException {
JDBCPool jdbcPool = new JDBCPool();
for (int i = 0; i < 9; i++) {
Connection connection = jdbcPool.getConnection();
System.out.println(connection);
connection.close();
}
}
}
8、程序的运行代码如下