JDBC 详解(干货分享)(三)

来源:互联网 发布:律所 知乎 编辑:程序博客网 时间:2024/06/05 00:44
  • 本文目录
1、应用程序直接获取连接的缺点(图解)2、使用数据库连接池优化程序性能(图解)3、可扩展增强某个类方法的功能的三种方式4、自定义数据库连接池——基于装饰设计模式5、数据库连接池核心代码——基于动态代理技术6、开源数据库连接池介绍7、DBCP数据源8、DBCP数据源与应用服务器整合使用——  配置Tomcat数据源9、C3P0 数据源10、JNDI技术简介 

学习小结
1、应用程序直接获取连接的缺点(图解)
这里写图片描述
2、使用数据库连接池优化程序性能(图解)
这里写图片描述
3、可扩展增强某个类方法的功能的三种方式
(1) 在实际开发中,发现对象的方法满足不了开发需求时,有三种方式对其进行增强:

a)创建该类的子类,并覆盖相应的方法;(较少使用)(b)使用装饰(包装)设计模式;(可以使用,但有时书写的方法太多)(c)使用动态代理技术。(最优的方式。)

(2) 使用子类覆盖父类方法的方式来增强功能的弊端:

需要将被增强父类的其他所需信息也要传递到子类中,而在开发中,经常无法知晓这些所需信息,所以使用子类覆盖被增强类方法的方式只是用于被增强类的

(3) 使用包装设计模式增强某个类方法的步骤:

(a) 定义一个类,实现与被增强类相同的接口;(b) 在类中定义一个变量,记住被增强对象;(c) 定义一个构造函数,接收被增强对象;(d) 覆盖想增强的方法;(e) 对于不想增强的方法,直接调用目标对象(被增强对象)的方法。

4、自定义数据库连接池——基于装饰设计模式
编写连接池需实现java.sql.DataSource接口。DataSource接口中定义了两个重载的getConnection方法:

Connection   getConnection()Connection  getConnection(String username, String password)

实现DataSource接口,并实现连接池功能的步骤:

(1) 在DataSource构造函数中批量创建与数据库的连接,并把创建的连接加入LinkedList对象中。(2) 实现getConnection方法,让getConnection方法每次调用时,从   LinkedList中取一个Connection返回给用户。(3) 当用户使用完Connection,调用Connection.close()方法时,Collection对象应保证将自己返回到LinkedList中,而不要把conn还给数据库。Collection保证将自己返回到LinkedList中是此处编程的难点。

Demo样例:自定义数据库连接池 (附件:JdbcPool.java)

public class JdbcPool implements DataSource {    private static LinkedList<Connection> list = new LinkedList<Connection>();    private static Properties config = new Properties();    static{    try {    config.load(JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("db.properties"));    // 以配置文件方式 读取数据库配置信息。     Class.forName(config.getProperty("driver"));     for(int i=0;i<10;i++){Connection conn = DriverManager.getConnection(config.getProperty("url"), config.getProperty("username"), config.getProperty("password"));list.add(conn);}} catch (Exception e) {throw new ExceptionInInitializerError(e); }}//conn.close()    此方法会将连接返回给数据库,所以不可用,需要自定义增强其功能,将连接返回到List集合中。/* 在实际开发,发现对象的方法满足不了开发需求时,有三种方式对其进行增强 * 1.写一个connecton子类,覆盖close方法,增强close方法 * 2.用包装设计模式 * 3.用动态代理    aop 面向切面编程*/public Connection getConnection() throws SQLException { if(list.size()<=0){ throw new RuntimeException("数据库忙,请稍会再来!!");}Connection conn = list.removeFirst();   //mysqlconnection   CMyConnection my = new MyConnection(conn);    调用自定义链接。return my;      //my-------preparedStatement   commit   createStatement  close} 
  • // 内部类,当然也可以使用外部类
class MyConnection implements Connection{      //1.定义一个类,实现与被增强相同的接口private Connection conn;      //2.在类中定义一个变量,记住被增强对象public MyConnection(Connection conn){    //3.定义一个构造函数,接收被增强对象this.conn = conn;}public void close(){     //4.覆盖想增强的方法list.add(this.conn);} 

//5.对于不想增强的方法,直接调用目标对象(被增强对象)的方法

public void clearWarnings() throws SQLException {   this.conn.clearWarnings();  } public void commit() throws   SQLException { this.conn.commit();  } public Statement   createStatement() throws SQLException { return   this.conn.createStatement(); 以下省略其他32个   不想增强的方法。(装饰模式的缺点,会实现许多不需要增强的方法) ………………  }    public Connection getConnection(String username, String   password)throws SQLException { // TODO Auto-generated method stub    return null; }     public PrintWriter getLogWriter() throws SQLException { // TODO   Auto-generated method stub return null; }  public int   getLoginTimeout() throws SQLException { // TODO Auto-generated method   stub return 0; }    public void setLogWriter(PrintWriter arg0) throws SQLException { //   TODO Auto-generated method stub  }    public void setLoginTimeout(int arg0) throws SQLException { // TODO   Auto-generated method stub   } }

5、数据库连接池核心代码——基于动态代理技术

使用动态代理技术构建连接池中的connectionproxyConn = (Connection) Proxy.newProxyInstance(this.getClass().getClassLoader(), conn.getClass().getInterfaces(),new InvocationHandler() {//此处为内部类,当close方法被调用时将conn还回池中,其它方法直接执行public Object invoke(Object proxy, Method method,Object[] args) throws Throwable {if (method.getName().equals("close")) {pool.addLast(conn);return null;}return method.invoke(conn, args); }}     );
  • 6、开源数据库连接池介绍

现在很多WEB服务器(Weblogic, WebSphere, Tomcat)都提供了DataSource的实现,即连接池的实现。通常我们把DataSource的实现,按其英文含义称之为数据源,数据源中都包含了数据库连接池的实现。
也有一些开源组织提供了数据源的独立实现:
DBCP 数据库连接池 (Tomcat内置)
C3P0 数据库连接池(Spring内置)
实际应用时不需要编写连接数据库代码,直接从数据源获得数据库的连接。程序员编程时也应尽量使用这些数据源的实现,以提升程序的数据库访问性能。
备注:简单地讲,使用开源数据源,就是为了获取其DataSource对象,然后通过该对象动态的地获取数据库连接。

  • 7、DBCP数据源

    DBCP 是 Apache 软件基金组织下的开源连接池实现,使用DBCP数据源,应用程序应在系统中增加如下两个 jar 文件:

Commons-dbcp.jar:连接池的实现Commons-pool.jar:连接池实现的依赖库

Tomcat 的连接池正是采用该连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。
使用DBCP示例代码:

static{InputStream in = JdbcUtil.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");Properties prop = new Properties();prop.load(in);BasicDataSourceFactory factory = new BasicDataSourceFactory();dataSource = factory.createDataSource(prop);}

Demo样例1:JDBC 数据库连接池 由应用程序独立使用。

public class JdbcUtils_DBCP { private static DataSource ds = null;static{try{InputStream in = JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("2013-06-10 11:26");Properties prop = new Properties();prop.load(in);             BasicDataSourceFactory factory = new BasicDataSourceFactory();ds = factory.createDataSource(prop);}catch (Exception e) { throw new ExceptionInInitializerError(e);}}public static Connection getConnection() throws SQLException{return ds.getConnection();} public static void release(Connection conn,Statement st,ResultSet rs){ if(rs!=null){try{rs.close();   //throw new }catch (Exception e) {e.printStackTrace();}rs = null;}if(st!=null){try{st.close();}catch (Exception e) {e.printStackTrace();}st = null; }if(conn!=null){try{conn.close(); }catch (Exception e) { e.printStackTrace();}} }}  

Demo样例2: dbcpconfig.properties 文件的内容(也可参见附件)

  • 连接设置
driverClassName=com.mysql.jdbc.Driverurl=jdbc:mysql://localhost:3306/day16username=rootpassword=root initialSize=10 maxActive=50 maxIdle=20 minIdle=5 maxWait=60000 #JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]  #注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。connectionProperties=useUnicode=true;characterEncoding=utf8 #指定由连接池所创建的连接的自动提交(auto-commit)状态。defaultAutoCommit=true #driver default 指定由连接池所创建的连接的只读(read-only)状态。 #如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)defaultReadOnly= #driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。 #可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLEdefaultTransactionIsolation=READ_COMMITTED
  • 8、DBCP数据源与应用服务器整合使用—— 配置Tomcat数据源
查看Tomcat文档(Tomcat-->TomcatDocument-->JNDI Resources-->定位到JDBC Data Sources),示例代码:<Context><Resource name="jdbc/datasource" auth="Container"  type="javax.sql.DataSource"username="root" password="root"  driverClassName="com.mysql.jdbc.Driver"url="jdbc:mysql://localhost:3306/jdbc"  maxActive="8" maxIdle="4"  /><Resource name="jdbc/EmployeeDB " auth="Container"  type="javax.sql.DataSource"username="root" password="root"  driverClassName="com.mysql.jdbc.Driver"url="jdbc:mysql://localhost:3306/jdbc"  maxActive="8" maxIdle="4"  /></Context>放置位置:在MyEclipse中,在目录/META-INF/下创建文件context.xml,将上述内容粘贴其中(注:不要写XML文件的指令头句)。该文件将被发布到Tomcat服务器的conf\Catalina\localhost目录中,并以工程名称 命名该文件。下面是调用JNDI,获取存储在JNDI容器中的资源的固定格式代码(有关JNDI的知识,参见下一章节):Context initCtx = new InitialContext();Context envCtx = (Context) initCtx.lookup("java:comp/env");dataSource = (DataSource)envCtx.lookup("jdbc/datasource");特别提醒:此种配置下,驱动jar文件需放置在tomcat的lib下可以创建工具类JDBCUtils,java来封装上面获取连接的代码。
  • Demo样例: 封装JNDI调用DataSource 获取连接的代码。
public class JdbcUtils_Tomcat {private static DataSource ds;static { try {Context initCtx = new InitialContext();Context envCtx = (Context) initCtx.lookup("java:comp/env");ds = (DataSource) envCtx.lookup("jdbc/EmployeeDB");} catch (Exception e) {throw new RuntimeException(e);} }public static Connection getConnection() throws SQLException{  return ds.getConnection();  }}
  • 9、C3P0 数据源

所需jar包:

c3p0-0.9.2-pre1.jarmchange-commons-0.2.jarc3p0-oracle-thin-extras-0.9.2-pre1.jar(注:连接oracle数据库时才导入,否则不用导入。)

(1) 使用独立程序编码的方式配置c3p0数据源(有关数据库的连接信息会被写入代码中,不够优雅)
步骤:

1、导入jar包:2、编码调用。
  • Demo:编码调用c3p0数据源获取连接。
ComboPooledDataSource ds=new ComboPooledDataSource();ds.setDriverClass("com.mysql.jdbc.Driver");ds.setJdbcUrl("jdbc:mysql://localhost:3306/jdbc1");ds.setUser("root");ds.setPassword("root");ds.setMaxPoolSize(40);ds.setMinPoolSize(10);ds.setInitialPoolSize(30);Connection conn=ds.getConnection(); 其他数据库的操作 ………
  • (2) 使用配置文件的方式配置c3p0数据源
(a) 配置文件名称:c3p0-config.xml (必须准确使用该名称)(b) 配置文件放置位置:src路径下或者是web-inf/classes都可以(都一样)。(c) 配置文件名称:c3p0-config.xml 的内容:<?xml version="1.0" encoding="UTF-8"?><c3p0-config><default-config>        <!-- 默认数据库连接池配置信息--><property name="driverClass">com.mysql.jdbc.Driver</property><property name="jdbcUrl">jdbc:mysql://localhost:3306/day16</property><property name="user">root</property><property name="password">root</property>                 <property name="acquireIncrement">5</property><property name="initialPoolSize">10</property><property name="minPoolSize">5</property><property name="maxPoolSize">20</property>    </default-config>
  • 第二个数据库连接池配置信息
<named-config name="flx">  <!--自定义数据源连接信息,比如可以分别书写 mysql  和 oracle 两个连接信息,以方便 换数据库。 注:此处对应调用代码中的“连接信息名” --><property name="driverClass">com.mysql.jdbc.Driver</property><property name="jdbcUrl">jdbc:mysql://localhost:3306/day16</property><property name="user">root</property><property name="password">root</property><property name="acquireIncrement">5</property><property name="initialPoolSize">10</property><property name="minPoolSize">5</property><property name="maxPoolSize">20</property></named-config></c3p0-config>

(d) 在页面的调用代码:

ComboPooledDataSource ds =new ComboPooledDataSource(连接信息名);备注:(a)此处对应 c3p0-config.xml 文件中 <named-config name="flx">  元素——指定名称的数据源。(b)数据源名称不写,则自动在类路径下搜索并使用 “默认数据源”;(c)若书写名称,同样会搜索并调用指定名称的数据源;

Demo样例:配置文件方式配置c3p0数据源的调用代码 。

public class JdbcUtils_C3P0 { private static ComboPooledDataSource ds = null;static{ try{ds = new ComboPooledDataSource();  //没指定数据源名称,则使用默认数据源}catch (Exception e) {throw new ExceptionInInitializerError(e);}}public static Connection getConnection() throws SQLException{return ds.getConnection();}public static void release(Connection conn,Statement st,ResultSet rs){ if(rs!=null){ try{rs.close();   //throw new }catch (Exception e) {e.printStackTrace();} rs = null;}if(st!=null){try{ st.close();}catch (Exception e) { e.printStackTrace(); }st = null;} if(conn!=null){try{conn.close();}catch (Exception e) {e.printStackTrace(); }  } } }  
  • 10、JNDI技术简介

JNDI(Java Naming and Directory Interface),Java命名和目录接口,它对应于J2SE中的javax.naming包,
这套API的主要作用在于:它可以把Java对象放在一个容器中(JNDI容器),并为容器中的java对象取一个名称,以后程序想获得Java对象,只需通过名称检索即可。
其核心API为Context,它代表JNDI容器,其lookup方法为检索容器中对应名称的对象。
此部分内容简单了解,会用其调用Tomcat内置的DBCP数据源即可。

0 0