数据库 -- 由数据库连接池引出的三种设计模式

笔记摘要:

     这里首先对数据库连接池的优化进行了说明,同时自己编写了一个数据库连接池,在实际开发中,为了获取标准的数据源,我们需要去实现javax.sal.DataSource接口,

     在实现过程中对于链接对象的close方法进行了不同的实现,以便在关闭close的时候,将连接对象放回连接池,而不是关闭掉,针对这一问题,提供了3种不同的解决

     方案,涉及了3种设计模式:装饰,适配器和代理。

 

一、直接获取连接与使用连接池的比较

应用程序直接获取连接示意图

 

 

缺点:

    用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、拓机。

 

 数据库连接池示意图

 

优势:

连接池中会有指定个数的连接对象,每次连接的时候,只要将请求发给连接池,连接池就会提供连接对象,使用完之后,再将连接对象放回连接池,

这样不用每次都去连接,大大提高了性能。

 

二、编写一个基本的连接池实现连接复用

 

原理:

通过一个LinkedList来模拟连接池,每次取Connection的时候,就remove,当释放的时候就再add进去,这里通过打印remove的前后,来说明每次使用之后会放回“连接池”

dbinfo.properties

className=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mydb
user=root
password=root

 

自定义连接池

复制代码
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.ResourceBundle;

import javax.management.RuntimeErrorException;

public class SimpleConnectionPool {
        private static String className;
        private static String url;
        private static String user;
        private static String password;

//创建一个集合用于模拟连接池
        private static LinkedList<Connection> pool = new LinkedList<Connection>();
//        private static LinkedList<Connection> pool =  (LinkedList<Connection>) Collections.synchronizedCollection(new LinkedList<Connection>());
        
//加载配置文件并注册驱动
        static{
            try {
                ResourceBundle bundle = ResourceBundle.getBundle("cn.itmonkey.util.dbinfo");
                className = bundle.getString("className");
                url = bundle.getString("url");
                user = bundle.getString("user");
                password = bundle.getString("password");
                Class.forName(className);
                
                //创建10个连接对象
                for(int i=0;i<10;i++){
                    Connection conn = DriverManager.getConnection(url,user,password);
                    pool.add(conn);
                }
                System.out.println("初始化连接");
                
                //打印所创建的连接对象
                int i=0;
                for(Connection conn: pool){
                    System.out.println(conn+"..."+i++);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        
        //获取连接对象,由于可能同时会有多个对象来取,所以使用同步
        public synchronized static Connection getConnection(){
            
            System.out.println("取之前的连接如下");
            int i=0;
            for(Connection conn: pool){
                System.out.println(conn+"..."+i++);
            }
            
            //从队列中移出一个连接对象,返回给调用者
            if(pool.size()>0){    //防止为0的时候,导致异常,所以要进行判断
                Connection conn = pool.remove();
                return conn;
            }
            else{
                throw new RuntimeException("对不起,服务器忙!");
            }
        }
        
        //将连接对象释放到队列中
        public static void release(Connection conn){
            pool.addLast(conn);
        }
}
复制代码

二、编写标准的数据库连接池

标准的数据源:

需要实现javax.sal.DataSource接口的类,标准的数据源里默认了维护了一个连接池

 

问题:

在直接获取标准数据源的Connection后,在调用它的close方法时,不是还回连接池中,而是直接关闭,我们希望在调用close方法的时候,是将Connection还回连接池中,而不是关闭

 

解决方案一:装饰设计模式

通过装饰,对需要的已有功能进行增强

 

装饰设计模式编写步骤

1、编写一个类,实现与被增强对象相同的接口

2、定义一个引用变量,记住被增强对象

3、定义构造方法,参数为接口后者父类,比便实现多态,传入被增强对象, 并给第2部的变量赋值

4、对于要增强的方法,自己改写

5、对于不需要增强的方法,调用原有对象的对应方法。

复制代码
public class MyConnection implements Connection{
    private Connection conn;
    private LinkedList<Connection> pool;//模拟连接池
    
    public MyConnection(Connection conn,LinkedList<Connection> pool){
        this.conn = conn;
        this.pool = pool;
    }
    
    //释放连接对象到连接池
    @Override
    public void close() throws SQLException {
        pool.add(conn);
    }

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

    
    @Override
    public void commit() throws SQLException {
        conn.commit();
    }
//不需要增强的方法,调用原有对象的对应方法即可,
    @Override
    public Array createArrayOf(String typeName, Object[] elements)
            throws SQLException {
        
        return conn.createArrayOf(typeName, elements);
    }
  //后面有很多方法,因为不需要增强的方法,调用原有对象的对应方法即可,这里略去
  
  …………
}
复制代码

解决方案二:适配器模式

 通过一个类去继承一个接口或父类,对需要增强的方法进行改写即可,适配器模式在监听机制中出现比较多,由于父类中有很多的抽象方法,如果一一实现,比较麻烦,所以通常在API中会提供一个已经默认实现的类,我们只需去继承这个类,然后对希望增强的方法进行复写即可

 

适配器模式编写步骤

1.编写一个类,继承默认适配器

2.定义一个引用变量,记住被增强对象

3.定义构造方法,传入被增强的对象,并给第2部的变量赋值

4.对于要增强的方法,自己改写

 

为Connection准备的适配器

 适配器本身也是一个包装类,实现或者继承一个类,但是什么都不做,所有的方法都调用原有对象的对应方法

 

复制代码
public class ConnectionWrapper implements Connection{
    protected Connection conn;
    public ConnectionWrapper(Connection conn){
        this.conn = conn;
    }
    
    @Override
    public void clearWarnings() throws SQLException {
        conn.clearWarnings();
    }

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

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

    @Override
    public Array createArrayOf(String typeName, Object[] elements)
            throws SQLException {
        return null;
    }
  …………
}
复制代码

 

继承适配器,对需要增强的方法复写即可

复制代码
public class MyConnection2  extends ConnectionWrapper {
    private LinkedList<Connection> pool;
    public MyConnection2(Connection conn, LinkedList<Connection> pool) {
        super(conn);
        this.pool = pool;
    }
//将连接对象还回池中
    public void close() throws SQLException {
        pool.add(conn);
    }
}
复制代码

解决方案三:动态代理

使用动态代理,在获取Connection的方法中使用代理,所以在获取Connection的时候,就是一个代理的Connection,该代理的Connection对象,会对调用方法进行判断,如果是close方法,就对返回值进行改写(这里是将Connection对象放回连接池中),否则就按照原来方法去执行

关于更多动态代理的知识,笔者在Java基础里面有详细地提到,其机制,实现等,想深刻了解的亲们可以查看:http://www.cnblogs.com/xushuai123/archive/2012/12/02/2978070.html

复制代码
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.util.LinkedList;
import java.util.ResourceBundle;

import javax.sql.DataSource;

//标准的数据源
public class ProxyDataSource implements DataSource{
    
    private static String className;// 驱动类名
    private static String url;// 连接串
    private static String user;
    private static String password;
    private static LinkedList<Connection> pool = new LinkedList<Connection>();
    static {
        try {
            ResourceBundle rb = ResourceBundle.getBundle("cn.itxushuai.util.dbinfo");
            className = rb.getString("className");
            url = rb.getString("url");
            user = rb.getString("user");
            password = rb.getString("password");
            Class.forName(className);

            // 初始化10个连接
            for (int i = 0; i < 10; i++) {
                Connection conn = DriverManager.getConnection(url, user,
                        password);
                pool.add(conn);
            }

        } catch (Exception e) {
            throw new ExceptionInInitializerError("驱动加载失败");
        }
    }
    @Override
    public synchronized Connection getConnection() throws SQLException {
        if(pool.size()>0){
            System.out.println("池中的连接如下");
        int i=1;
        for(Connection conn : pool){
            System.out.println(conn+"----"+i++);
        }
        
        final Connection conn = pool.remove();
        Connection proxyConn = (Connection)Proxy.newProxyInstance(conn.getClass().getClassLoader(), conn.getClass().getInterfaces(), 
                new InvocationHandler(){

                    @Override
                    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);
                        }
                    }}
                );    
            return proxyConn;
        }else{
            throw new RuntimeException("服务器忙");
        }
        }

    @Override
    public Connection getConnection(String username, String password)
            throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int getLoginTimeout() throws SQLException {
        // TODO Auto-generated method stub
        return 0;
    }

    @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 boolean isWrapperFor(Class<?> iface) throws SQLException {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

}
复制代码

posted on 2013-10-08 18:20  Java码界探秘  阅读(149)  评论(0编辑  收藏  举报

导航