用动态代理实现连接池

用动态代理实现连接池

1、用javax.sql.DataSource实现连接池。

       DataSource是标准的数据源。所在连接数据库的工具类,都应该实现这个接口。

此接口中,只有二个方法:

   这两个方法 ,都是用来获取连接的:

Connection

getConnection()
          尝试建立与此 DataSource 对象所表示的数据源的连接。

 Connection

getConnection(String username, String password)
          尝试建立与此 DataSource 对象所表示的数据源的连接。

      

 

4.1、三种实现

DataSource 接口由驱动程序供应商实现。共有三种类型的实现:

  1. 基本实现 - 生成标准的 Connection 对象
    1. 即这个DataSource中只有一个连接。
  2. 连接池实现 - 生成自动参与连接池的 Connection 对象。此实现与中间层连接池管理器一起使用。
    1. 还需要使用第三方jar包。
  3. 分布式事务实现 - 生成一个 Connection 对象,该对象可用于分布式事务,大多数情况下总是参与连接池。此实现与中间层事务管理器一起使用,大多数情况下总是与连接池管理器一起使用。
    1. 当你连接多个数据库时。要求也可以实现事务。
    2. JTA – Java Transaction Arichi..标准。
      1. javax.sql.XADataSource (接口)

 

 

 

 

找一下,哪些类,实现了javax.sql.DataSource接口

 

4.1、如何实现

 

      

 

测试使用mysql连接的ds类:

    @Test

    public void test1() throws Exception{

       Class.forName("com.mysql.jdbc.Driver");

       MysqlDataSource ds =

              new MysqlDataSource();

       ds.setUrl("jdbc:mysql:///oracle?chracterEncoding=UTF8");

       ds.setUser("root");

       ds.setPassword("1234");

       Connection con = ds.getConnection();

       System.err.println("这是第1个:"+con);

       //再测试获取第二个

       con.close();

       Connection con2 = ds.getConnection();

       System.err.println("这是第二个:"+con2);

       System.err.println("--------------------------");

    }

上面的代码不能使用:

    1:依赖于mysql这个jar包的。

       2:mysq中没有实现池管理。只是每次调用getConnection获取一个新的连接。

 

 

4.3、自己实现标准的连接池

       1:必须要实现javax.sql.DataSource接口。

       2:必须在实现类中,实现池管理.

       3:保证可以回收连接。我就是动态代理实现。

       4:一个项目中,保证只有一个DataSource实例就可以了

 

 

 

有了ds对象以后,ds就是一个数据源了.

 

 

第一步:开发一个资源文件

 

第二步:开发MyDataSource类

package cn.oracle.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.logging.Logger;

import javax.sql.DataSource;

/**

 * 不写static

 * @author Administrator 应该让用户提供连接数据库需要信息

 */

public class MyDataSource implements DataSource {

    // 非静态的

    private LinkedList<Connection> pool = new LinkedList<Connection>();

    // 提供一个构造方法

    // 在构造中,让用提供四个

    public MyDataSource(String driver, String url, String user, String pwd)

           throws Exception {

       this(driver, url, user, pwd, 1);

    }

    public MyDataSource(String driver, String url, String user, String pwd,

           int pooSize) throws Exception {

       Class.forName(driver);

       for(int i=0;i<pooSize;i++){

           Connection con = DriverManager.getConnection(url,user,pwd);

           //将三个原生的conn放到

           pool.addLast(con);

       }

    }

    @Override

    public Connection getConnection() throws SQLException {

       synchronized (pool) {

           if(pool.size()==0){

              try {

                  pool.wait();

              } catch (InterruptedException e) {

                  e.printStackTrace();

              }

              return getConnection();

           }else{

              final Connection con = pool.removeFirst();

              Object obj =

                     Proxy.newProxyInstance(MyDataSource.class.getClassLoader(),

                            new Class[]{Connection.class},

                            new InvocationHandler() {

                                @Override

                                public Object invoke(Object proxy, Method method, Object[] args)

                                       throws Throwable {

                                   if(method.getName().equals("close")){

                                       synchronized (pool) {

                                          pool.add(con);

                                          pool.notify();

                                          return null;

                                       }

                                   }else{

                                       //放行,调用原生的

                                       return method.invoke(con, args);

                                   }

                                }

                            });

              System.err.println("size si:"+pool.size());

              return (Connection) obj;

           }

       }

    }

    //还有些方法就不用实现了

}

 

实现一个工具类,对MyDataSource实现工厂方式获取:

package cn.oracle.utils;

import java.sql.Connection;

import java.util.Properties;

import javax.sql.DataSource;

/**

 */

public class DataSourceUtils {

    private static DataSource ds;

    static {

       try {

           Properties p = new Properties();

           p.load(ClassLoader.getSystemResourceAsStream("jdbc.properties"));

           ds = new MyDataSource(p.getProperty("driver"),

                  p.getProperty("url"), p.getProperty("user"),

                  p.getProperty("password"), Integer.parseInt(p

                         .getProperty("size")));

 

       } catch (Exception e) {

           throw new RuntimeException(e);

       }

    }

    //提供一个返回一个ds的方法

    public static DataSource getDatasSource(){

       return ds;

    }

    //还可以提供一个方法

    public static Connection getCon() throws Exception{

       return ds.getConnection();

    }

}

代码结构如下:

 

 

 

测试如下:

 

 

5、使用第三方连接池

       DBCP

       C3p0

       研究它们是用什么实现回收连接的?是包装还是代理。

 

 

5.1、DBCP

       DataBase Connection Pool

 

 

使用dbcp连接数据库

 创建dbcp数据源:

\

 

第一步:导入dbcpjar包及它的依赖包

 

 

 

第二步:dbcp核心

       必须是DataSource的子类

 

用BasicDataSouce连接数据库:

 

 

直接在类中声明:

    BasicDataSource ds =

              new BasicDataSource();

       //找set

       ds.setDriverClassName("com.mysql.jdbc.Driver");

       ds.setUsername("root");

       ds.setPassword("1234");

       ds.setUrl("jdbc:mysql:///oracle?characterEncoding=UTF8");

       //看有几个连接

       ds.setInitialSize(3);//设置开始时有几个连接

       ds.setMaxActive(3);//设置最多有几个连接

 

或是通过配置文件读取:

    、

 

还是像

 

 

 

开发获取dbcp数据源的工具类:

package cn.oracle.utils;

import java.sql.Connection;

import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;

import org.apache.commons.dbcp.BasicDataSourceFactory;

public class DBCPUtils {

    private static DataSource ds;

    static {

       try {

           Properties p = new Properties();

           p.load(ClassLoader.getSystemResourceAsStream("bb.properties"));

           ds = BasicDataSourceFactory.createDataSource(p);

       } catch (Exception e) {

 

       }

    }

    public static DataSource getDs(){

       return ds;

    }

    public static Connection getCon() throws Exception{

       return ds.getConnection();

    }

}



 

 

包装与代理:

       由于包装就只两个实例类在进行交互。

       反射比较慢。

      

      

 

 

6、元数据分析

      

      

       只通过一个connection可以分析,目前数据库的所有信息:

              版本

              连接是什么数据库

        驱动器版本

        获取这个数据库中有多少表.

 

 

 

6.1、用DataBaseMetaData分析数据库的连接

           /**

     * 用元数据信息分析某个连接的数据库中有多少表

     */

    @Test

    public void getTables() throws Exception{

       Connection con = DBCPUtils.getConnection();

       DatabaseMetaData db = con.getMetaData();

       //获取表的所有表名

       //第一个参数与第二个参数是是指数据库是什么

       //第三个参数你查找表名,

       //找什么对象

       ResultSet rs = db.getTables("mysql","mysql",null,new String[]{"TABLE"});

       //显示表名

       while(rs.next()){

           String tname = rs.getString("TABLE_NAME");

           System.err.println(tname);

       }

    }

 

6.2、分析结果集的信息

       通过一个查询获取表中列,字段名类型 信息

 

       请显示出mysql库中的user表的所有信息

    @Test

    public void testTable() throws Exception{

       String sql = "select host,db,user from mysql.db";

       //获取连接

       Connection con = DBCPUtils.getConnection();

       Statement st = con.createStatement();

       //先获取到结果集

       ResultSet rs = st.executeQuery(sql);

       //获取结果集的元信息

       ResultSetMetaData rsmd = rs.getMetaData();

       //获取这个结果一共有几列

       int cols = rsmd.getColumnCount();//5[微软用户1] 

       //获取列名

       for(int i=1;i<=cols;i++){

           String cName = rsmd.getColumnName(i);[微软用户2] 

           System.err.print(cName+"\t\t");

       }

       System.err.println();

       while(rs.next()){

           //一行数据

           for(int i=1;i<=cols;i++){

              String data = rs.getString(i);

              System.err.print(data+"\t\t");

           }

           System.err.println();

       }

    }

 

 

 

你可能感兴趣的:(动态代理)