在前面的文章中已经说过使用连接池的很多好处和优势,也曾讨论过怎么使用数据库连接池,不过那时用的都是别人写好的一些DataSource类。现在我们自己来写一个数据库连接池,下面使用两种方法来实现,这里分别用到了两种设计模式,即Decorator(包装模式)和Proxy(代理模式)(关于其他的模式在后续的学习过程中都会一一介绍,敬请关注),首先来看第一种实现方法,也就是使用Decorator设计模式:
我们在这里引入一个ConnectionDecortor类,代码如下,此方法的功能不多介绍,看名字就知道是包装了Connection接口,对此接口的方法都做了简单的实现:
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Savepoint;
import java.sql.Statement;
import java.util.Map;
public class ConnectionDecorator implements Connection {
Connection conn;
public ConnectionDecorator(Connection conn) {
this.conn = conn;
}
public Statement createStatement() throws SQLException {
return conn.createStatement();
}
public PreparedStatement prepareStatement(String arg0) throws SQLException {
return conn.prepareStatement(arg0);
}
public CallableStatement prepareCall(String arg0) throws SQLException {
return conn.prepareCall(arg0);
}
public String nativeSQL(String arg0) throws SQLException {
return conn.nativeSQL(arg0);
}
public void setAutoCommit(boolean arg0) throws SQLException {
conn.setAutoCommit(arg0);
}
public boolean getAutoCommit() throws SQLException {
return conn.getAutoCommit();
}
public void commit() throws SQLException {
conn.commit();
}
public void rollback() throws SQLException {
conn.rollback();
}
public void close() throws SQLException {
conn.close();
}
public boolean isClosed() throws SQLException {
return conn.isClosed();
}
public DatabaseMetaData getMetaData() throws SQLException {
return conn.getMetaData();
}
public void setReadOnly(boolean arg0) throws SQLException {
conn.setReadOnly(arg0);
}
public boolean isReadOnly() throws SQLException {
return conn.isReadOnly();
}
public void setCatalog(String arg0) throws SQLException {
conn.setCatalog(arg0);
}
public String getCatalog() throws SQLException {
return conn.getCatalog();
}
public void setTransactionIsolation(int arg0) throws SQLException {
conn.setTransactionIsolation(arg0);
}
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 arg0, int arg1) throws SQLException {
return conn.createStatement(arg0, arg1);
}
public PreparedStatement prepareStatement(String arg0, int arg1, int arg2)
throws SQLException {
return conn.prepareStatement(arg0, arg1, arg2);
}
public CallableStatement prepareCall(String arg0, int arg1, int arg2)
throws SQLException {
return conn.prepareCall(arg0, arg1, arg2);
}
public Map<String, Class<?>> getTypeMap() throws SQLException {
return conn.getTypeMap();
}
public void setTypeMap(Map<String, Class<?>> arg0) throws SQLException {
conn.setTypeMap(arg0);
}
public void setHoldability(int arg0) throws SQLException {
conn.setHoldability(arg0);
}
public int getHoldability() throws SQLException {
return conn.getHoldability();
}
public Savepoint setSavepoint() throws SQLException {
return conn.setSavepoint();
}
public Savepoint setSavepoint(String arg0) throws SQLException {
return conn.setSavepoint(arg0);
}
public void rollback(Savepoint arg0) throws SQLException {
conn.rollback(arg0);
}
public void releaseSavepoint(Savepoint arg0) throws SQLException {
conn.releaseSavepoint(arg0);
}
public Statement createStatement(int arg0, int arg1, int arg2)
throws SQLException {
return conn.createStatement(arg0, arg1, arg2);
}
public PreparedStatement prepareStatement(String arg0, int arg1, int arg2,
int arg3) throws SQLException {
return conn.prepareStatement(arg0, arg1, arg2, arg3);
}
public CallableStatement prepareCall(String arg0, int arg1, int arg2,
int arg3) throws SQLException {
return conn.prepareCall(arg0, arg1, arg2);
}
public PreparedStatement prepareStatement(String arg0, int arg1)
throws SQLException {
return conn.prepareStatement(arg0, arg1);
}
public PreparedStatement prepareStatement(String arg0, int[] arg1)
throws SQLException {
return conn.prepareStatement(arg0, arg1);
}
public PreparedStatement prepareStatement(String arg0, String[] arg1)
throws SQLException {
return conn.prepareStatement(arg0, arg1);
}
}
从代码可以看出我们使用这个类只是对传入的Connection加上了一个外壳。
定义一个接口ConnectionPool,此接口只有两个方法,即getConnection()和releaseConnection():
import java.sql.Connection;
import java.sql.SQLException;
public interface ConnectionPool {
Connection getConnection() throws SQLException;
void releaseConnection(Connection conn);
}
下面DBConnectionPool类是对上面接口的实现:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Vector;
public class DBConnectionPool implements ConnectionPool {
//一个存储连接对象的集合,即上面所谓的连接池
private static Vector<Connection> pool;
//最多建立20个连接
private final int POOL_MAX_SIZE = 20;
//根据连接池中是否有连接对象来进行处理
public synchronized Connection getConnection() throws SQLException {
if (pool == null)
pool = new Vector<Connection>();
Connection conn = null;
//如果没有连接对象则新建一个
if (pool.isEmpty())
conn = createConnection();
else {
//如果有连接对象则取一个出来返回给客户端
int last_idx = pool.size() - 1;
conn = pool.get(last_idx);
pool.remove(pool.get(last_idx));
}
return new PooledConnection(this,conn);
}
public synchronized void releaseConnection(Connection conn) {
if (conn instanceof PooledConnection && pool.size() > POOL_MAX_SIZE) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
} else {
pool.add(conn);
}
}
//创建连接的方法
private Connection createConnection() {
try {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql:///Student", "root", "");
return conn;
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
}
下面的PooledConnection类中我们会对包装类ConnectionDecorator的close()方法做了一些特殊的处理,并不是直接关闭它,而是交给ConnectionPool 的 releaseConnection()去处理。
import java.sql.Connection;
import java.sql.SQLException;
public class PooledConnection extends ConnectionDecorator implements Connection {
private ConnectionPool connPool;
public PooledConnection(ConnectionPool connPool, Connection conn) {
super(conn);
this.connPool = connPool;
}
public void close() throws SQLException{
connPool.releaseConnection(this.conn);
}
}
至此第一种方法就实现了。下面来看第二种方法,即使用Proxy模式来实现:
ConnectionHandler 实现jdk提供的InvocationHandler 接口,重写了invoke方法。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
public class ConnectionHandler implements InvocationHandler {
Connection conn;
ConnectionPool pool;
public ConnectionHandler(ConnectionPool pool) {
this.pool = pool;
}
//bind方法是将动态代理绑定到指定的Connection。返回一个绑定代理后的Connection
public Connection bind(Connection conn) {
this.conn = conn;
Connection proxyConn = (Connection) Proxy.newProxyInstance(conn
.getClass().getClassLoader(), conn.getClass().getInterfaces(),
this);
return proxyConn;
}
//方法拦截器,判断当前调用的方法是否“close”方法,是则调用ConnectionPool的releaseConnection()方法
//否则直接调用Connection的方法。
public Object invoke(Object arg0, Method arg1, Object[] arg2)
throws Throwable {
Object object = null;
if ("close".equals(arg1.getName())) {
pool.releaseConnection(conn);
} else {
object = arg1.invoke(conn, arg2);
}
return object;
}
}
然后把上面的DBConnectionPool.getConnection()方法做一点小小的修改:
//此方法返回值为一个经过绑定的连接
public synchronized Connection getConnection() throws SQLException {
if (pool == null)
pool = new Vector<Connection>();
Connection conn = null;
if (pool.isEmpty())
conn = createConnection();
else {
int last_idx = pool.size() - 1;
conn = pool.get(last_idx);
pool.remove(pool.get(last_idx));
}
ConnectionHandler handler = new ConnectionHandler(this);
return handler.bind(conn);
}
可以看出,基于Proxy模式的实现相对于Decorator模式的更加简洁了。