JavaWeb-JDBC连接池、JDBC框架

来源:互联网 发布:网络涉黄举报 编辑:程序博客网 时间:2024/05/17 11:58
一、连接池概念
  1. 应用程序直接获取连接的缺点

    • 出现的问题
      • 用户请求Servlet,Servlet请求Service,Service调用Dao。
      • 用户请求多次,Dao需要打开关闭connection多次。
      • 频繁的开关connection很多次,浪费时间和消耗资源,并且容易造成数据库服务器内存溢出、宕机。
  2. 连接池图示

  3. 解决方法-连接池
    1. 解决方法:连接资源有限,需要重复利用,而不是关闭。怎么重复利用,连接池。
    2. 连接池:连接池是一个pool(缓存),里面封装了一个数据或者集合,这里使用List集合。
    3. 过程:每次应用启动的时候,创建一片缓存,即连接池,连接池中创建一定的connection对象。用户访问的时候从连接池取出一个connection连接,当使用结束后需要还回这个connection。

二、编写连接池遇到的问题
  1. 要对connection进行归还,需要重写connection的close方法。调用close的时候,归还这个conenction到连接池中。
  2. 对一个已有类com.mysql.Connection(不能修改)中的某个方法进行增强或者更改它的行为,直接继承可以吗?
    1. 直接继承在这里不可以,因为close方法中,归还的时候,父类的一些参数(URL、username、password)不能一起归还。
    2. com.mysql.Connection这个接口中的方法,都没有实现。(这里看一下Connection是哪个包中的,还有是接口还是类。)
  3. 简单的连接池:自己编写的简单连接池太简单,实际开发中不能满足需求。
三、编写数据库连接池的原理。
  1. 编写连接池需实现javax.sql.DataSource接口
    1. 只有实现了这个接口,才是一个标准的数据连接池(数据源)
    2. 框架中就是使用这个接口的实现得到连接。上面简单的连接池没有实现,框架没法调用所以不规范。
  2. DataSource接口中定义了两个重载的getConnection方法:
    1. Connection  getConnection() 
    2. Connection getConnection(String username, String password)  (基本都不实现)
  3. 总体思路
    1. 实现DataSource接口,并实现连接池功能的步骤:
    2. 在DataSource构造函数中批量创建与数据库的连接,并把创建的连接加入LinkedList对象中。
    3. 实现getConnection方法,让getConnection方法每次调用时,从LinkedList中取一个Connection返回给用户。
    4. 当用户使用完Connection,调用Connection.close()方法时,Collection对象应保证将自己返回到LinkedList中,而不要把conn还给数据库。
    5. Collection保证将自己返回到LinkedList中是此处编程的难点。 
四、连接池的实现
  1. 使用包装设计模式,包装:com.mysql.jdbc.Connection
    1. 编写一个类,实现与被包装类相同的接口或继承被包装类
    2. 定义一个私有变量,记住被包装对象的引用,LinkedList<Connection>对象
    3. 定义构造方法,传入被包装对象的引用
    4. 对于要增强的方法,只管重写
    5. 对于不需要增强的方法,调用原有对象的原有方法。、
  2. 包装设计模式的实现
    1. 思路:
      1. 默认适配器实现了Connection接口,子类继承了默认适配器
      2. 用户要连接的时候,拿到的是适配器的子类
      3. 适配器的子类重写了close方法,并且引用了Connection对象和LinkedList<Connection>对象。当调用close时,把Connection对象添加到集合中。
      4. 下面为参考:
      5. 调用Connection对象的close()方法时, 为阻止系统销毁该连接, 而改为将连接归还给连接池, 可以采用包装设计模式. 此时包装的目标类为Connection, 因此需要定义MyConnection类并实现Connection接口. MyConnection类应该存在2个成员: Connection对象和LinkedList<Connection>对象, 这2个成员可以通过构造函数存入. 同时需要覆写close()方法, 在close()方法中实现将Connection添加到集合中.
    2. 核心代码
  3. 动态代理
      • 编写一个与目标类具有相同接口的代理类,当外界访问被代理类时,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。 
      • 代理类的缺陷是,只能代理接口中的方法。
    1. 思路:
      1. 用户要连接的时候,不给他原有对象,而是给一个代理(proxy)对象
      2. 这个代理类是com.mysql.jdbc.Connection的代理类$Proxy0
      3. 三个参数及用途
        1. 代理类的类加载器是所在的类的类加载器
        2. 代理类与被代理对象conn有相同的接口conn.getClass().getInterfaces(),所以有相同的行为。
        3. 代理类通过内部类invoke拦截想要的方法close,把conn返回池中
    2. 核心代码;
  4. 动态代理模式的实现
    1. 思路:
      1. 在内存中创建一集合,其中包含固定数目的连接。
      2. 获取连接时,直接从集合中获取集合对象。
      3. 关闭连接时,将不同的集合对象再加入集合中.
      4. 要遵循先入先出的原则(LinkedList.removeLast方法)
    2. 核心代码
      1. proxyConn = (Connection) Proxy.newProxyInstance(this.getClass()
      2. .getClassLoader(), conn.getClass().getInterfaces(),
      3. new InvocationHandler() {
      4. //此处为内部类,当close方法被调用时将conn还回池中,其它方法直接执行
      5. public Object invoke(Object proxy, Method method,
      6.   Object[] args) throws Throwable {
      7. if (method.getName().equals("close")) {
      8. pool.addLast(conn);
      9. return null;
      10. }
      11. return method.invoke(conn, args);
      12. }
      13. });
五、开源的连接池使用
  • 现在很多WEB服务器(Weblogic, WebSphere, Tomcat)都提供了DataSoruce的实现,即连接池的实现。通常我们把DataSource的实现,按其英文含义称之为数据源,数据源中都包含了数据库连接池的实现。
  • 也有一些开源组织提供了数据源的独立实现:
    • DBCP 数据库连接池 
    • C3P0 数据库连接池
  • 实际应用时不需要编写连接数据库代码,直接从数据源获得数据库的连接。程序员编程时也应尽量使用这些数据源的实现,以提升程序的数据库访问性能。
  1. DBCP():全称是Data Base Connectivity Pool
    • DBCP 是 Apache 软件基金组织下的开源连接池实现,使用DBCP数据源,应用程序应在系统中增加如下两个 jar 文件:
    • Commons-dbcp.jar:连接池的实现
    • Commons-pool.jar:连接池实现的依赖库
    1. 使用DBCP
      1. static{
      2. InputStream in = JdbcUtil.class.getClassLoader().
      3. getResourceAsStream("dbcpconfig.properties");
      4. Properties prop = new Properties();
      5. prop.load(in);

      6. BasicDataSourceFactory factory = new BasicDataSourceFactory();
      7. dataSource = factory.createDataSource(prop);
      8. }
    2. <-这是DBCP使用的配置文件
    3. 原理是用的包装
  2. C3P0()
    • 原理是用的代理、
六、Tomcat管理数据源
  1. Tomcat 的连接池是采用 DBCP连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。
  2. 前提:拷贝数据库的驱动到Tomcat\lib目录下
  3. 在应用的META-INF目录下建立一个名字为context.xml的配置文件,内容如下:
    1.   <?xml version="1.0" encoding="UTF-8"?>
    2. <Context>
    3.  <Resource name="jdbc/day14" auth="Container" type="javax.sql.DataSource"
    4.                maxActive="30" maxIdle="10" maxWait="10000"
    5.                username="root" password="sorry" driverClassName="com.mysql.jdbc.Driver"
    6.                url="jdbc:mysql://localhost:3306/day14"/>
    7.   </Context>
    • 注:经过以上配置,容器启动时就是会创建好数据源。
  4. 获取数据源并使用。(JNDI的API)Java Naming and Directory Interface Java命名与目录服务
    1. Context initContext = new InitialContext();
    2. DataSource ds = (DataSource)initContext.lookup("java:/comp/env/jdbc/day14");
    3. Connection conn = ds.getConnection();
  5. 特别提醒:此种配置下,驱动jar文件需放置在tomcat的lib下
  6. JNDI简介:
    1. JNDI(Java Naming and Directory Interface),Java命名和目录接口,它对应于J2SE中的javax.naming包,
    2. 这套API的主要作用在于:它可以把Java对象放在一个容器中(JNDI容器),并为容器中的java对象取一个名称,以后程序想获得Java对象,只需通过名称检索即可。
    3. 其核心API为Context,它代表JNDI容器,其lookup方法为检索容器中对应名称的对象。

七、获取数据库的元信息(编写框架用)
  • 元数据:数据库、表、列的定义信息。
  1. 元数据- DataBaseMetaData
    1. 获得:Connection.getMetaData()
      1. 返回 DataBaseMetaData对象
    2. DataBaseMetaData对象
      1. getURL():返回一个String类对象,代表数据库的URL。
      2. getUserName():返回连接当前数据库管理系统的用户名。
      3. getDatabaseProductName():返回数据库的产品名称。
      4. getDatabaseProductVersion():返回数据库的版本号。
      5. getDriverName():返回驱动驱动程序的名称。
      6. getDriverVersion():返回驱动程序的版本号。
      7. isReadOnly():返回一个boolean值,指示数据库是否只允许读操作。
  2. 元数据- ParameterMetaData 
    1. 获得:PreparedStatement . getParameterMetaData() : 
      1. 返回代表PreparedStatement元数据的ParameterMetaData对象。(可得到有几个问号)  
      2. Select * from user where name=? And password=?
    2. ParameterMetaData对象
      1. getParameterCount() :获得指定参数的个数
      2. getParameterType(int param) :获得指定参数的sql类型(驱动可能不支持)
  3. 元数据- ResultSetMetaData
    1. ResultSet. getMetaData() 
      1. 返回代表ResultSet对象元数据的ResultSetMetaData对象。 
    2. ResultSetMetaData对象
      1. getColumnCount() :返回resultset对象的列数
      2. getColumnName(int column): 获得指定列的名称,从第一列开始
      3.  getColumnTypeName(int column):获得指定列的类型 java.sql.Types
  • 元数据的应用-使用元数据简化JDBC代码
  • 业务背景:系统中所有实体对象都涉及到基本的CRUD操作:
  • 所有实体的CUD操作代码基本相同,仅仅发送给数据库的SQL语句不同而已,因此可以把CUD操作的所有相同代码抽取到工具类的一个update方法中,并定义参数接收变化的SQL语句。
  • 实体的R操作,除SQL语句不同之外,根据操作的实体不同,对ResultSet的映射也各不相同,因此可义一个query方法,除以参数形式接收变化的SQL语句外,可以使用策略模式由qurey方法的调用者决定如何把ResultSet中的数据映射到实体对象中。(Bean,BeanList,Long)
  • 核心代码
  1. String columnName = md.getColumnName(i);
  2. Object columnData = rs.getObject(i);
  3. Field feild = clazz.getDeclaredField(columnName);
  4. feild.setAccessible(true);
  5. feild.set(obj, columnData);
八、自定义JDBC框架
  1. 策略设计模式
  2. 反射和泛型
原创粉丝点击