JDBC

来源:互联网 发布:服装之星v8数据库补丁 编辑:程序博客网 时间:2024/06/16 06:24


前言
         最近在研究Mybatis框架,由于该框架基于JDBC,想要很好地理解和学习Mybatis,必须要对JDBC有较深入的了解。所以便把JDBC 这个东东翻出来,好好总结一番,作为自己的笔记,也是给读者一个参考~~~以下是本文的组织结构,读者可以点击上面的目录查看:

概述       
      一般情况下,在应用程序中进行数据库连接,调用JDBC接口,首先要将特定厂商的JDBC驱动实现加载到系统内存中,然后供系统使用。基本结构图如下:
    

驱动加载入内存的过程
    这里所谓的驱动,其实就是实现了Java.sql.Driver接口的类。如Oracle的驱动类是 oracle.jdbc.driver.OracleDriver.class(此类可以在oracle提供的JDBC jar包中找到),此类实现了java.sql.Driver接口。
由于驱动本质上还是一个class,将驱动加载到内存和加载普通的class原理是一样的:使用Class.forName("driverName")。以下是将常用的数据库驱动加载到内存中的代码:
[java] view plain copy
 print?
  1. //加载Oracle数据库驱动  
  2. Class.forName("oracle.jdbc.driver.OracleDriver");  
  3.   
  4. //加载SQL Server数据库驱动  
  5. Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");  
  6.   
  7. //加载MySQL 数据库驱动  
  8. Class.forName("com.mysql.jdbc.Driver");  

         注意:Class.forName()将对应的驱动类加载到内存中,然后执行内存中的static静态代码段,代码段中,会创建一个驱动Driver的实例,放入DriverManager中,供DriverManager使用。
      例如,在使用Class.forName() 加载oracle的驱动oracle.jdbc.driver.OracleDriver时,会执行OracleDriver中的静态代码段,创建一个OracleDriver实例,然后调用DriverManager.registerDriver()注册:
[java] view plain copy
 print?
  1. static {  
  2.     Timestamp localTimestamp = Timestamp.valueOf("2000-01-01 00:00:00.0");  
  3.     try {  
  4.         if (defaultDriver == null) {  
  5.             //创建一个OracleDriver实例,然后注册到DriverManager中  
  6.                                defaultDriver = new OracleDriver();  
  7.             DriverManager.registerDriver(defaultDriver);  
  8.         }  
  9.   
  10.     } catch (RuntimeException localRuntimeException) {  
  11.     } catch (SQLException localSQLException) {  
  12.     }  

Driver的功能
      java.sql.Driver接口规定了Driver应该具有以下功能:

其中:
acceptsURL(String url) 方法用来测试对指定的url,该驱动能否打开这个url连接。driver对自己能够连接的url会制定自己的协议,只有符合自己的协议形式的url才认为自己能够打开这个url,如果能够打开,返回true,反之,返回false;
例如:oracle定义的自己的url协议如下:
jdbc:oracle:thin:@//<host>:<port>/ServiceName
jdbc:oracle:thin:@<host>:<port>:<SID>
oracle自己的acceptsURL(String url)方法如下:
[java] view plain copy
 print?
  1. public boolean acceptsURL(String paramString) {  
  2.     if (paramString.startsWith("jdbc:oracle:")) {  
  3.         return (oracleDriverExtensionTypeFromURL(paramString) > -2);  
  4.     }  
  5.   
  6.     return false;  
  7. }  
  8.   
  9. private int oracleDriverExtensionTypeFromURL(String paramString) {  
  10.     int i = paramString.indexOf(58) + 1;  
  11.   
  12.     if (i == 0) {  
  13.         return -2;  
  14.     }  
  15.     int j = paramString.indexOf(58, i);  
  16.   
  17.     if (j == -1) {  
  18.         return -2;  
  19.     }  
  20.     if (!(paramString.regionMatches(true, i, "oracle"0, j - i))) {  
  21.         return -2;  
  22.     }  
  23.     ++j;  
  24.   
  25.     int k = paramString.indexOf(58, j);  
  26.   
  27.     if (k == -1) {  
  28.         return -3;  
  29.     }  
  30.     String str = paramString.substring(j, k);  
  31.   
  32.     if (str.equals("thin")) {  
  33.         return 0;  
  34.     }  
  35.     if ((str.equals("oci8")) || (str.equals("oci"))) {  
  36.         return 2;  
  37.     }  
  38.   
  39.     return -3;  
  40. }  
     由上可知oracle定义了自己应该接收什么类型的URL,自己能打开什么类型的URL连接(注意:这里acceptsURL(url)只会校验url是否符合协议,不会尝试连接判断url是否有效) 。拓展阅读:常用数据库 JDBC URL格式
connect(String url,Properties info)方法,创建Connection对象,用来和数据库的数据操作和交互,而Connection则是真正数据库操作的开始(在此方法中,没有规定是否要进行acceptsURL()进行校验)。
手动加载驱动 Driver 并实例化进行数据库操作的例子
[java] view plain copy
 print?
  1. public static void driverTest(){  
  2.     try {  
  3.         //1.加载oracle驱动类,并实例化  
  4.         Driver driver = (Driver) Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();  
  5.   
  6.         //2.判定指定的URL oracle驱动能否接受(符合oracle协议规则)  
  7.         boolean flag = driver.acceptsURL("jdbc:oracle:thin:@127.0.0.1:1521:xe");  
  8.         //标准协议测试  
  9.         boolean standardFlag1 = driver.acceptsURL("jdbc:oracle:thin:@//<host>:<port>/ServiceName");  
  10.         boolean standardFlag2 = driver.acceptsURL("jdbc:oracle:thin:@<host>:<port>:<SID>");  
  11.         System.out.println("协议测试:"+flag+"\t"+standardFlag1+"\t"+standardFlag2);  
  12.           
  13.         //3.创建真实的数据库连接:  
  14.         String  url = "jdbc:oracle:thin:@127.0.0.1:1521:xe";  
  15.         Properties props = new Properties();  
  16.         props.put("user""louluan");  
  17.         props.put("password""123456");  
  18.         Connection connection = driver.connect(url, props);  
  19.         //connection 对象用于数据库交互,代码省略。。。。。  
  20.           
  21.     } catch (Exception e) {  
  22.         System.out.println("加载Oracle类失败!");  
  23.         e.printStackTrace();  
  24.     } finally{  
  25.           
  26.     }  
  27. }  
   
       上述的手动加载Driver并且获取连接的过程稍显笨拙:如果现在我们加载进来了多个驱动Driver,那么手动创建Driver实例,并根据URL进行创建连接就会显得代码杂乱无章,并且还容易出错,并且不方便管理。JDBC中提供了一个DriverManager角色,用来管理这些驱动Driver。
DriverManager角色
         事实上,一般我们操作Driver,获取Connection对象都是交给DriverManager统一管理的。DriverManger可以注册和删除加载的驱动程序,可以根据给定的url获取符合url协议的驱动Driver或者是建立Conenction连接,进行数据库交互。
  
    以下是DriverManager的关键方法摘要:
    

    DriverManager 内部持有这些注册进来的驱动 Driver,由于这些驱动都是 java.sql.Driver 类型,那么怎样才能获得指定厂商的驱动Driver呢?答案就在于:
      java.sql.Driver接口规定了厂商实现该接口,并且定义自己的URL协议。厂商们实现的Driver接口通过acceptsURL(String url)来判断此url是否符合自己的协议,如果符合自己的协议,则可以使用本驱动进行数据库连接操作,查询驱动程序是否认为它可以打开到给定 URL 的连接。
使用DriverManager获取指定Driver
     对于驱动加载后,如何获取指定的驱动程序呢?这里,DriverManager的静态方法getDriver(String url)可以通过传递给的URL,返回可以打开此URL连接的Driver。
     比如,我想获取oracle的数据库驱动,只需要传递形如jdbc:oracle:thin:@<host>:<port>:<SID>或者jdbc:oracle:thin:@//<host>:<port>/ServiceName的参数给DriverManager.getDriver(String url)即可:
[java] view plain copy
 print?
  1. Driver oracleDriver =DriverManager.getDriver("jdbc:oracle:thin:@<host>:<port>:<SID>");  
     实际上,DriverManager.getDriver(String url)方法是根据传递过来的URL,遍历它维护的驱动Driver,依次调用驱动的Driver的acceptsURL(url),如果返回acceptsURL(url)返回true,则返回对应的Driver:
[java] view plain copy
 print?
  1. public static Driver getDriver(String paramString) throws SQLException {  
  2.   
  3.     //省略部分代码。。。。  
  4.     Iterator localIterator = registeredDrivers.iterator();  
  5.     //遍历注册的驱动  
  6.     while (localIterator.hasNext()) {  
  7.         DriverInfo localDriverInfo = (DriverInfo) localIterator.next();  
  8.         if (isDriverAllowed(localDriverInfo.driver, localClass))  
  9.             try {  
  10.                 //如果accepsURL() 为true,返回对应的driver  
  11.                 if (localDriverInfo.driver.acceptsURL(paramString)) {  
  12.                     //返回对应的driver  
  13.                     return localDriverInfo.driver;  
  14.                 }  
  15.             } catch (SQLException localSQLException) {  
  16.             }  
  17.         else  
  18.             println("    skipping: "+ localDriverInfo.driver.getClass().getName());  
  19.     }  
  20.     throw new SQLException("No suitable driver""08001");  
  21.     //-----省略部分代码  
  22. }  

使用DriverManager注册和取消注册驱动Driver
      在本博文开始的 驱动加载的过程一节中,讨论了当使用Class.forName("driverName")加载驱动的时候,会向DriverManager中注册一个Driver实例。以下代码将验证此说法:
[java] view plain copy
 print?
  1. public static void defaultDriver(){  
  2.     try {  
  3.           
  4.         String url = "jdbc:oracle:thin:@127.0.0.1:1521:xe";  
  5.           
  6.         //1.将Driver加载到内存中,然后执行其static静态代码,创建一个OracleDriver实例注册到DriverManager中  
  7.         Class.forName("oracle.jdbc.driver.OracleDriver");  
  8.         //取出对应的oracle 驱动Driver  
  9.         Driver driver  = DriverManager.getDriver(url);  
  10.         System.out.println("加载类后,获取Driver对象:"+driver);  
  11.           
  12.         //将driver从DriverManager中注销掉  
  13.         DriverManager.deregisterDriver(driver);  
  14.         //重新通过url从DriverManager中取Driver  
  15.         driver  = DriverManager.getDriver(url);  
  16.         System.out.println(driver);  
  17.           
  18.     } catch (Exception e) {  
  19.         System.out.println("加载Oracle类失败!");  
  20.         e.printStackTrace();  
  21.     } finally{  
  22.           
  23.     }  
  24. }  
以上代码主要分以下几步:
1. 首先是将  oracle.jdbc.driver.OracleDriver加载到内存中;
 2.  然后便调用DriverManager.getDriver()去取Driver实例;
3.  将driver实例从DriverManager中注销掉;
4.尝试再取 对应url的Driver实例;
上述代码执行的结果如下:

      从执行结果看,正好能够验证以上论述:当第四步再次获取对应url的 Driver 实例时,由于已经被注销掉了,找不到适当的驱动Driver,抛出了 "Not suitable driver" 的异常。
     将上述的例子稍作变化,在注销掉了静态块创建的driver后,往DriverManager注册一个自己创建的Driver对象实例(具体步骤请看注释):
[java] view plain copy
 print?
  1. public static void defaultDriver(){  
  2.     try {  
  3.           
  4.         String url = "jdbc:oracle:thin:@127.0.0.1:1521:xe";  
  5.           
  6.         //1.将Driver加载到内存中,然后执行其static静态代码,创建一个OracleDriver实例注册到DriverManager中  
  7.         Driver dd = (Driver)Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();  
  8.         //2.取出对应的oracle 驱动Driver  
  9.         Driver driver  = DriverManager.getDriver(url);  
  10.         System.out.println("加载类后,获取Driver对象:"+driver);  
  11.           
  12.         //3. 将driver从DriverManager中注销掉  
  13.         DriverManager.deregisterDriver(driver);  
  14.           
  15.         //4.此时DriverManager中已经没有了驱动Driver实例,将创建的dd注册到DriverManager中  
  16.         DriverManager.registerDriver(dd);  
  17.           
  18.         //5.重新通过url从DriverManager中取Driver  
  19.         driver  = DriverManager.getDriver(url);  
  20.                         
  21.                        System.out.println("注销掉静态创建的Driver后,重新注册的Driver:    "+driver);  
  22.         System.out.println("driver和dd是否是同一对象:" +(driver==dd));  
  23.     } catch (Exception e) {  
  24.         System.out.println("加载Oracle类失败!");  
  25.         e.printStackTrace();  
  26.     } finally{  
  27.           
  28.     }  
  29. }  
以下代码运行的结果:
  以上代码先创建了一个Driver对象,在注销了DriverManager中由加载驱动过程中静态创建驱动之后,注册到系统中,现在DriverManager中对应url返回的Driver 即是在代码中创建的Driver对象。
使用DriverManager创建 Connection 连接对象
      创建 Connection 连接对象,可以使用驱动Driver的 connect(url,props),也可以使用 DriverManager 提供的getConnection()方法,此方法通过url自动匹配对应的驱动Driver实例,然后调用对应的connect方法返回Connection对象实例。
[java] view plain copy
 print?
  1. Driver driver  = DriverManager.getDriver(url);  
  2.             Connection connection = driver.connect(url, props);  
上述代码等价于:
[java] view plain copy
 print?
  1. Class.forName("oracle.jdbc.driver.OracleDriver");  
  2. Connection connection = DriverManager.getConnection(url, props);  
jdbc.drivers
      DriverManager 作为 Driver 的管理器,它在第一次被使用的过程中(即在代码中第一次用到的时候),它会被加载到内存中,然后执行其定义的static静态代码段,在静态代码段中,有一个  loadInitialDrivers()  静态方法,用于加载配置在jdbc.drivers 系统属性内的驱动Driver,配置在jdbc.drivers 中的驱动driver将会首先被加载:
[java] view plain copy
 print?
  1. static {  
  2.     loadInitialDrivers();//加载配置在jdbc.drivers系统变量中的驱动driver  
  3.     println("JDBC DriverManager initialized");  
  4.     SET_LOG_PERMISSION = new SQLPermission("setLog");  
  5. }  
[java] view plain copy
 print?
  1. private static void loadInitialDrivers() {  
  2.     String str1;  
  3.     try {  
  4.         str1 = (String) AccessController  
  5.                 .doPrivileged(new PrivilegedAction() {  
  6.                     public String run() {  
  7.                         return System.getProperty("jdbc.drivers");//返回jdbc.drivers值  
  8.                     }  
  9.                 });  
  10.     } catch (Exception localException1) {  
  11.         str1 = null;  
  12.     }  
  13.   
  14.                //省略部分代码......  
  15.     if ((str1 == null) || (str1.equals("")))  
  16.         return;  
  17.     String[] arrayOfString1 = str1.split(":");  
  18.     println("number of Drivers:" + arrayOfString1.length);  
  19.     for (String str2 : arrayOfString1)  
  20.         try {  
  21.             //Class.forName加载对应的driver  
  22.             Class.forName(str2, true, ClassLoader.getSystemClassLoader());  
  23.         } catch (Exception localException2) {  
  24.             println("DriverManager.Initialize: load failed: "  
  25.                     + localException2);  
  26.         }  
  27. }  
以下是通过jdbc.drivers加载驱动driver的方式:
[java] view plain copy
 print?
  1. String url = "jdbc:oracle:thin:@127.0.0.1:1521:xe";  
  2. //设置值系统变量jdbc.drivers  
  3. System.setProperty("jdbc.drivers""oracle.jdbc.driver.OracleDriver");  
  4. //2.通过特定的url获取driver  
  5. Driver driver = DriverManager.getDriver(url);  
  6. //打印是否存在  
  7. System.out.println(driver);  
请记住:一定要在第一次使用DriverManager之前设置jdbc.drivers,因为DriverManager中的static静态代码段只会被执行一次!


JDBC的工作原理
 
http://blog.sina.com.cn/s/blog_634c33eb01010tmi.html
转载
标签: 杂谈
分类: java
工作原理流程:装载驱动程序---->获得数据库连接---->使用Statement或PreparedStatement执行SQL语句---->
              返回执行的结果---->关闭相关的连接
JDBC 原理浅析
一.本节目的:
了解JDBC的概念及工作原理
写在详解前
对JAVA基础知识的回顾:
JAVA接口:
是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,
因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。
通过接口可以实现不相关的类的相同行为.
通过接口可以指明多个类需要实现的方法.
通过接口可以了解对象的交互方法而不需要了解对象所对应的类.
二.JDBC 概念:
     什么是JDBC?
     通俗地说:JDBC是java和关系数据库的之间的桥梁;
     根据"百科名片"所描述:
     JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序,同时,JDBC也是个商标名。
三.JDBC组成:
    JDBC有两部分组成:JDBC API和JDBC Driver Interface.
    JDBC API:是提供给开发者的一组独立于数据库的API,对任何数据库的操作,都可以用这组API来进行.那么要把这些通用的API翻译成特定数据库能懂的"指令",就要由JDBC Driver Interface来实现了;
    JDBC Driver Interface 是面向JDBC驱动程序开发商的编程接口,它会把我们通过JDBC API发给数据库的通用指令翻译给他们自己的数据库.
    为了使客户端程序独立于特定数据库驱动程序,JDBC规范建议开发者使用接口编程方式,即尽量使应用依赖java.sql 及javax.sql中的接口和类.
   如下图:
四.JDBC驱动程序类型:
    <1>.JDBC-ODBC桥加ODBC驱动程序:需要ODBC驱动,适合于企业网或三层结构应用程序(通过将JDBC调用委托给ODBC接口)
    <2>.本地API:需要驱动程序的二进制代码支持(一部分java编写,一部分委托给数据库客户端代码实现)
    <3>.JDBC网络纯java驱动程序:将JDBC转换为与DBMS无关的网络协议,又被某服务器转换为一种DBMS协议,以操作各种数据库(驱动程序由中间件服务器提供)
    <4>.本地协议纯java驱动程序:将JDBC调用直接转换成JDBC所使用的网络协议 (全部由JAVA实现,直接和数据库访问)
五.详解JDBC原理:
<1>. 结构:
 DriverManager (是一工厂实现类,用了工厂方法模式)
    |
 Dirver  (是驱动程序对象的接口,指向具体数据库驱动程序对象=DriverManager.getDriver(String URL))
    |
 Connection (是连接对象接口,指向具体数据库连接对象=Drivermanager.getConnection(String URL))
    |
 Statement (执行静态SQL语句接口,=Connection.CreateStatement())
    |
 ResultSet  (是指向结果集对象的接口,=Statement.excuteXXX())
<2>.工作原理:
1.装载驱动程序:(实例化时自动向DriverManager注册(DriverManager.registerDriver())
 <1>.Class.forName(driver)
      <2>.Class.forName(driver).newInstance()
       <3>.new driver()
2.取得数据库连接(Connect to the DataBase)
 <1>.用DriverManager取数据库连接
     Connection cn = DriverManager.getConnection(url,uid,pwd);
 <2>.用jndi(java的命名和目录服务)方式:多用于jsp
     Context ctx = (Context) new InitialContext().lookup("java:comp/env");
               DataSource ds = (DataSource) ctx.lookup(jndi);
     Connection cn = ds.getConnection();
3.执行sql语句(Execute the SQL)
 <1>.用Statement来执行sql语句(主要是不带参的SQL)
               Statement sm = cn.createStatement();
     sm.executeQuery(sql); // 执行数据查询语句(select)
     sm.executeUpdate(sql); // 执行数据更新语句(delete、update、insert、drop等)
 <2>.用PreparedStatement来执行sql语句(带参SQL)
     String  sql  = "insert into CUSTOMER(id,name) values (?,?)";
               PreparedStatement ps = cn.prepareStatement(sql);
              ps.setInt(1,xxx);
               ps.setString(2,xxx);
              ...
              ResultSet rs = ps.executeQuery(); // 查询
              int c = ps.executeUpdate(); // 更新
 <3>.用PreparedStatement来执行sql语句(批量更新或删除SQL)
     PreparedStatement pstmt  =  con.prepareStatement("UPDATE EMPLOYEES  SET SALARY = ? WHERE ID = ?");
        for(int i =0;i<length;i++){
                 pstmt.setBigDecimal(1, param1[i]);
                 pstmt.setInt(2, param2[i]);
                 pstmt.addBatch();
         }
         pstmt. executeBatch();
 <4>.用CallablePrepareStatement来调用存储过程
     CallableStatement cstmt = con.prepareCall("{call getTestData(?, ?)}");
     cstmt.registerOutParameter(1, java.sql.Types.TINYINT);
     cstmt.registerOutParameter(2, java.sql.Types.DECIMAL, 3);
     cstmt.executeQuery();
     byte x = cstmt.getByte(1);
     java.math.BigDecimal n = cstmt.getBigDecimal(2, 3);
4.事务的处理(JDBC的事务处理简单,在执行多条更新语句后,加cn.commit()或cn.rollback()就可以)
 <1>.关闭Connection的自动提交
     connection.setAutoCommit(false);
 <2>.执行一系列sql语句:执行新sql前,以前的Statement(或PreparedStatemet)须close
     Statement sm ;
           sm = cn.createStatement(insert into user...);
           sm.executeUpdate();
           sm.close();
           sm = cn.createStatement("insert into corp...);
           sm.executeUpdate();
           sm.close();
 <3>.提交
     cn.commit();
 <4>.如果发生异常,回滚:
     cn.rollback();
5.处理执行结果:
 <1>.查询语句,返回记录集ResultSet
 <2>.更新语句,返回数字,表示该更新影响的记录数(0,表示未更新,-1表示更新失败)
 <3>.ResultSet的方法:while(re.next())
         next(),将游标往后移动一行,如果成功返回true;否则返回false
         getInt("id")或getSting("name"),返回当前游标下某个字段的值
6.关闭数据库连接
 rs.close();
 ps.close(); /stat.close();
 con.close();
完!!!

原创粉丝点击