实现自己的标准数据库连接池
对于一个简单的数据库应用,由于对于数据库的访问不是很频繁。这时可以简单地在需要访问数据库时,就新创建一个连接,用完后就关闭它,这样做也不会带来什 么明显的性能上的开销。但是对于一个复杂的数据库应用,情况就完全不同了。频繁的建立、关闭连接,会极大的减低系统的性能,因为对于连接的使用成了系统性 能的瓶颈。
对于共享资源,有一个很著名的设计模式:资源池。该模式正是为了解决资源频繁分配、释放所造成的问题的。把该模式应用到数据库连接管理领域,就是建立一个数据库连接池,提供一套高效的连接分配、使用策略,最终目标是实现连接的高效、安全的复用。
原理:
连接池基本的思想是在系统初始化的时候,将数据库连接作为对象存储在内存中,当用户需要访问数据库时,并非建立一个新的连接,而是从连接池中取出一个已建立的空闲连接对象。使用完毕后,用 户也并非将连接关闭,而是将连接放回连接池中,以供下一个请求访问使用。而连接的建立、断开都由连接池自身来管理。同时,还可以通过设置连接池的参数来控 制连接池中的初始连接数、连接的上下限数以及每个连接的最大使用次数、最大空闲时间等等。也可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。
实现:
java中实现标准的连接池需要实现DataSource接口,实现类又称为数据源。
实现类,需要连接数据库并维持一定数量的数据库连接,在getConnection方法中获取Connection连接
问题是当一个系统用完Connection连接后,DataSource接口中并没有提供release方法去释放Connection连接。(这里的释放并不是真正的连接的释放,而是将连接重新放入连接池中,以保证连接池的数量。试想如果是真正的释放,连接池用几次就没连接了,废了)
这时就需要我们自己去实现将用完的连接重新加入连接池。
close是Connection接口的实现类中的方法,实现类是你用的数据库驱动,当需要对一个已知类的方法进行扩展时,可以通过继承丶包装模式丶动态代理这些方法进行扩展(别想着在别人的jar包里修改close方法,不允许也不能这样做),即需要对Connection接口的实现类中的close方法进行扩展。
比如你连得mysql,实现类就是mysql的驱动(注意版本不同,数据库不同,实现方式都会有所差异)
比如我用的mysql-connector-java-5.1.7-bin.jar版本,实现类为com.mysql.jdbc.JDBC4Connection
考虑继承
java中实现数据源(就是连接池)的标准接口是DataSource(即要求所有数据源都必须实现该接口),所有java中用到数据源的框架接受的类型都是DataSource类型,如果你实现了自己的标准数据源MyDataSource(实现了DataSource接口),你又写了一个类继承了JDBC4Connection比如叫做MyConnection,你把MyDataSource传入框架,然后调用MyConnection的close方法,这样看似可以?问题是MyDataSource获取的Connection对象和MyConnection关闭的Connection对象根本不是同一个对象,所有继承在此行不通。更何况有的驱动版本Connection实现类是final类型不可继承。
考虑装饰(包装)模式
装饰模式是完全可以的,利用组合的方式将Connection接口传入装饰者的构造函数里,利用多态将实现类传进来,对close方法进行扩展,其他方法调用实现类中的方法。由于装饰模式前面的博客已经学习过,这里就不在累赘了。
考虑动态代理(推荐)
动态代理就是利用反射将Connection中的方法交给代理类去做,这样在调用close方法时可以通过代理类实现对close方法进行扩展
下面上动态代理解决方案
标准数据源
这里在一个properties文件中写数据库相关信息,在实现类中获取数据库连接信息
package util; import java.io.InputStream; 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; public class MyDateSource implements DataSource { private static String className; private static String url; private static String userName; private static String password; private static LinkedList<Connection> pool = new LinkedList<Connection>(); static { try { InputStream is = JDBCUtil.class.getClassLoader().getResourceAsStream("mypro.properties"); Properties p = new Properties(); p.load(is); className = p.getProperty("className"); url = p.getProperty("url"); userName = p.getProperty("userName"); password = p.getProperty("password"); Class.forName(className);
//维持了10个数据库连接 for (int i = 0; i < 10; i++) { Connection conn = DriverManager.getConnection(url, userName, password); pool.add(conn); } } catch (Exception e) { e.printStackTrace(); } } //获取Connection连接 public Connection getConnection() throws SQLException { if(pool.size()>0){ Connection conn= pool.remove();
//返回Connection的代理对象,并对close方法进行扩展,将连接放回连接池 return (Connection) Proxy.newProxyInstance(conn.getClass().getClassLoader(),new Class[]{Connection.class}, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if("close".equals(method.getName())){ return pool.add(conn); }else{ return method.invoke(conn, args); } } }); }else{ System.out.println("sorry,server is busy"); } return null; } @Override public PrintWriter getLogWriter() throws SQLException { // TODO Auto-generated method stub return null; } @Override public void setLogWriter(PrintWriter out) throws SQLException { // TODO Auto-generated method stub } @Override public void setLoginTimeout(int seconds) throws SQLException { // TODO Auto-generated method stub } @Override public int getLoginTimeout() throws SQLException { // TODO Auto-generated method stub return 0; } @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException { // TODO Auto-generated method stub return null; } @Override public <T> T unwrap(Class<T> iface) throws SQLException { // TODO Auto-generated method stub return null; } @Override public boolean isWrapperFor(Class<?> iface) throws SQLException { // TODO Auto-generated method stub return false; } @Override public Connection getConnection(String username, String password) throws SQLException { // TODO Auto-generated method stub return null; } }
这样自己的标准连接池就实现了,在需要用到连接的地方直接使用,用完调用close方法时代理类会将连接放回连接池。