JDBC板块精华整理20051226


重要提示:本文部分内容是斑竹从网上搜集整理而来,如果您认为该文档的内容侵犯了您的权益,请与整理者(excelarthur@yahoo.com.cn)联系,与dev2dev网站无关。

JDBC3.0的特性

1、JDBC3.0规范中数据库连接池框架
  JDBC3.0规范中通过提供了一个支持数据库连接池的框架,这个框架仅仅规定了如何支持连接池的实现,而连接池的具体实现JDBC 3.0规范并没有做相关的规定。通过这个框架可以让不同角色的开发人员共同实现数据库连接池。

  通过JDBC3.0规范可以知道具体数据库连接池的实现可以分为JDBC Driver级和Application Server级。在JDBC Driver级的实现中任何相关的工作均由特定数据库厂商的JDBC Drvier的开发人员来具体实现,即JDBC Driver既需要提供对数据库连接池的支持同时也必须对数据库连接池进行具体实现。而在Application Server级中数据库连接池的实现中特定数据库厂商的JDBC Driver开发人员和Application Server开发人员来共同实现数据库连接池的实现(但是现在大多数Application Server厂商实现的连接池的机制和规范中提到有差异),其中特定数据库厂商的JDBC Driver提供数据库连接池的支持而特定的Application Server厂商提供数据库连接池的具体实现。

  JDBC3.0规范规定了如下的类和接口来支持数据库连接池的实现。

javax.sql.ConnectionEvent 
javax.sql.ConnectionPoolDataSource 
javax.sql.PooledConnection 
javax.sql.ConnectionEventListener

  其中除javax.sql.ConnectionEvent是类,其它的均为接口。

  

JDBC3.0连接池框架的关系图

  通过此图可以大概的了解相关接口在一个典型的三层环境中应用程序的位置。

2、检索自动产生的关键字
  为了解决对获取自动产生的或自动增加的关键字的值的需求,JDBC 3.0 API 现在将获取这种值变得很轻松。要确定任何所产生的关键字的值,只要简单地在语句的 execute() 方法中指定一个可选的标记,表示您有兴趣获取产生的值。您感兴趣的程度可以是 Statement.RETURN_GENERATED_KEYS,也可以是 Statement.NO_GENERATED_KEYS。在执行这条语句后,所产生的关键字的值就会通过从 Statement 的实例方法 getGeneratedKeys() 来检索 ResultSet 而获得,ResultSet 包含了每个所产生的关键字的列,下面的示例创建一个新的作者并返回对应的自动产生的关键字。

……
Statement stmt = conn.createStatement(); 
// Obtain the generated key that results from the query. 
stmt.executeUpdate("INSERT INTO authors " + 
"(first_name, last_name) " + 
"VALUES (‘Ghq', ‘Wxl')", 
Statement.RETURN_GENERATED_KEYS); 
ResultSet rs = stmt.getGeneratedKeys(); 
if ( rs.next() ) { 
// Retrieve the auto generated key(s). 
int key = rs.getInt(); 
}
……

3、返回多重结果
  JDBC 2 规范的一个局限是,在任意时刻,返回多重结果的语句只能打开一个ResultSet。作为 JDBC 3.0 规范中改变的一个部分,规范将允许 Statement 接口支持多重打开的 ResultSets。然而,重要的是 execute() 方法仍然会关闭任何以前 execute() 调用中打开的 ResultSet。所以,要支持多重打开的结果,Statement 接口就要加上一个重载的 getMoreResults() 方法。新式的方法会做一个整数标记,在 getResultSet() 方法被调用时指定前一次打开的 ResultSet 的行为。接口将按如下所示定义标记:

  CLOSE_ALL_RESULTS:当调用 getMoreResults() 时,所有以前打开的 ResultSet 对象都将被关闭。

  CLOSE_CURRENT_RESULT:当调用 getMoreResults() 时,当前的 ResultSet 对象将被关闭。

  KEEP_CURRENT_RESULT:当调用 getMoreResults() 时,当前的 ResultSet 对象将不会被关闭。

  下面展示的是一个处理多重打开结果的示例。

…… 
String procCall; 
// Set the value of procCall to call a stored procedure. 
// … 
CallableStatement cstmt = connection.prepareCall(procCall); 
int retval = cstmt.execute(); 
if (retval == false) { 
// The statement returned an update count, so handle it. 
// … 
} else { // ResultSet 
ResultSet rs1 = cstmt.getResultSet(); 
// … 
retval = cstmt.getMoreResults(Statement.KEEP_CURRENT_RESULT); 
if (retval == true) { 
ResultSet rs2 = cstmt.getResultSet(); 
// Both ResultSets are open and ready for use. 
rs2.next(); 
rs1.next(); 
// … 
 } 
}
……

4、在事务中使用 Savepoint
  也许在 JDBC 3.0 中最令人兴奋的附加特点就是 Savepoint 了。JDBC 2 中的事务支持让开发人员可以控制对数据的并发访问,从而保证持续数据总是保持一致的状态。可惜的是,有时候需要的是对事务多一点的控制,而不是在当前的事务中简单地对每一个改变进行回滚。在JDBC 3.0 下,可以通过 Savepoint 获得这种控制。Savepoint 接口允许您将事务分割为各个逻辑断点,以控制有多少事务需要回滚。下图将说明如何在事务中运用 Savepoint。

  

Savepoint 的直观表示
  你或许不是经常需要使用 Savepoint。然而,在一种普遍的情况下 Savepoint 会发挥作用,那就是您需要作一系列的改变,但是在知道所有的结果之前不能确定应该保留这些改变的哪一部分。下面的代码示例说明了如何使用 Savepoint 接口。

......
conn.setAutoCommit(false); 
// Set a conservative transaction isolation level. 
conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); 
Statement stmt = conn.createStatement(); 
int rows = stmt.executeUpdate( "INSERT INTO authors " + 
" (first_name, last_name) VALUES " + 
" ('Ghq', 'Wxl')"); 
// Set a named savepoint. 
Savepoint svpt = conn.setSavepoint("NewAuthor"); 
// … 
rows = stmt.executeUpdate( "UPDATE authors set type = 'fiction' " + 
"WHERE last_name = 'Wxl'"); 
// … 
conn.rollback(svpt); 
// … 
// The author has been added, but not updated. 
conn.commit(); 
......

5、其他的特性
  1)元数据 API
  元数据 API 已经得到更新,DatabaseMetaData 接口现在可以检索 SQL 类型的层次结构,一种新的 ParameterMetaData 接口可以描述 PreparedStatement 对象中参数的类型和属性。

  2)CallableStatements 中已命名的参数
  在 JDBC 3.0 之前,设置一个存储过程中的一个参数要指定它的索引值,而不是它的名称。 CallableStatement 接口已经被更新了,现在您可以用名称来指定参数。

  3)数据类型的改变
  JDBC 所支持的数据类型作了几个改变,其中之一是增加了两种新的数据类型。

  为了便于修改 CLOB(Character Large OBject,字符型巨对象)、BLOB(Binary Large OBject,二进制巨对象)和 REF(SQL 结构)类型的值,同名的数据类型接口都被更新了。接下来的是,因为我们现在能够更新这些数据类型的值,所以 ResultSet 接口也被修改了,以支持对这些数据类型的列的更新,也包括对 ARRAY 类型的更新。增加的两种新的数据类型是 java.sql.Types.DATALINK 和 java.sql.Types.BOOLEAN。新增的数据类型指的是同名的 SQL 类型。DATALINK 提供对外部资源的访问或 URL,而 BOOLEAN 类型在逻辑上和 BIT 类型是等同的,只是增加了在语义上的含义。DATALINK 列值是通过使用新的 getURL() 方法从 ResultSet 的一个实例中检索到的,而 BOOLEAN 类型是通过使用 getBoolean() 来检索的。

二进制大对象Blob
  Blob对象是SQL Blob的Java语言映射。SQL Blob是一个内置类型,它可以将一个二进制大对象保存在数据库中。接口ResultSet、CallableStatement和PreparedStatement中的方法允许程序员使用与访问SQL 92内置类型同样的方式来访问SQL 99类型BLOB。

  在标准实现中,JDBC驱动程序在后台使用SQL类型LOCATOR(BLOB)来实现Blob接口。LOCATOR(BLOB)指向保存在数据库服务器上的SQL BLOB值,而且这些操作作用在这个LOCATOR(定位器)上与作用在BLOB值本身有同样的结果。这意味着用户可以在一个Blob实例上执行操作而不必将这个BLOB数据物化到用户上,这将显著的提高性能。因为驱动程序在后台使用LOCATOR(BLOB),所以它的使用对程序员是完全透明的。

  Blob实例的标准行为一直保持有效,直到这个事务(创建一个Blob的事务)执行了提交或者回滚操作。

  1. 创建Blob对象
      下面的代码说明了如何创建一个Blob对象,其中stmt是一个Statement对象:
    Statement stmt = con..createStatement(ResultSet.TYPE_INSENSITIVE,
           ResultSet.CONCUR_READ_ONLY);
    ResultSet rs = stmt.excuteQuery(“SELECT DATA FROM TABLE	1”);
    If (rs.next()){
      rs.first();
      Blob blob=rs.getBlob(“DATA”);
    } 
    
    变量blob包含一个指向BLOB值的逻辑指针,该BLOB值保存在结果集rs的第一行的DATA列中。即使变量blob实际上并不包含BLOB值中的数值,应用程序在blob上执行操作仍然像在实际的数据上执行一样。即应用程序在blob上所作的任何操作都会对表中的BLOB值起作用。
  2. 物化BLOB数据
      开发人员可以在Blob对象上调用JDBC API中的方法,就像这些方法直接
      在该对象所指向的SQL BLOB上执行操作一样。然而,如果想在BLOB数据上执行操作,就必须首先将BLOB数据物化到客户。Blob接口提供了两个方法来物化BLOB数据:getBinaryStream,这个方法将BLOB数据物化为一个输入流;getBytes,这个方法将BLOB值得一部分或者全部物化为一个字节数组。下面的代码说明了如何将Blob所指向的BLOB值得全部物化为一个输入流:
     java.io.InputStream in = blob.getBinaryStream();
      byte b;
      while((b = in.read()) >-1){
       System.out.println(b);
    }
    
    接下来的代码同样物化了blob所指向的BLOB值得所有数据,但是它产生的是字节数组而不是输入流。
     long len = blob.length();
      byte [] data = blob.getBytes(1,len);
      for(int i=0;i<len;i++){
       byte b = data[i];
       System.out.println(b);
    }
    
      变量data复制了blob所指向的BLOB值的所有字节。这是因为传递给方法getBytes的参数值说明了整个BLOB值:第一个参数表示从第一个字节开始返回字节,第二个参数说明它返回的字节长度是BLOB值的长度。
      需要说明的是,因为SQL和Java语言之间的不同,一个BLOB值得第一个字节在位置1,而Java数组的第一个元素的索引是0。
  3. 存储Blob值
      若要在数据库中存储Blob值,应用程序可以把它作为一个参数传递给
      PreparedStatement的方法setBlob。下面的代码就实现了这个功能:
    PreparedStatement的方法setBlob。下面的代码就实现了这个功能:
      Blob stats = rs.getBlob(“STATS”);
      PreparedStatement pstmt= con.preparedStatement(
    “UPDATE SIGHTINGS SET MEAS= ? WHERE AREA = ‘BEIJING' ”);
      pstmt.setBlob(1,stats);
      pstmt.excuteUpdate();
    
  4. 发现Blob对象中的模式
      如果一个Blob对象包含一个给定的字节集合,应用程序可以使用方法
      position的两个方法来找到它。其中一个方法搜索一个给定的字节数组,而另一个在一个Blob对象中搜索一个给定的Blob对象。如果发现一个匹配的结果,则返回该模式字节的起始位置。
  5. 修改Blob对象的方法
      JDBC 3.0 API中新增的方法setBytes和setBinaryStream允许应用程序对
      Blob对象进行修改。
      方法setBytes有两个方法来向Blob对象添加数据。其中一个方法增加给定的字节数组的全部内容,而另一个方法增加给定字节数组的特定部分。两个方法都使用一个参数说明向Blob对象插入数据的起始位置。例如,下面的代码段在一个Blob对象blob1的第一个字节处写入整个字节数组bytes。在这种情况下,bytes包含了Blob对象blob的所有字节,因此执行的结果是blob2被写入了blob1的起始处。需要注意的是如果blob2的长度是1024字节,那么blob2的1024各字节将覆盖blob1的开头的1024各字节。
    byte [] bytes = blob2.getBytes(1,blob2.length());
      blob.setBytes(1,bytes,0,512);
    
      下面的代码段说明如何仅仅向Blob对象加入一个字节数组的特定部分。在这种情况下,方法setBytes接受两个附加的参数来说明需要增加字节数组的哪一个部分。其中一个参数指明了这个字节数组的起始偏移量,另一个参数说明这个字节数组包含多少个连续的字节。
    byte [] bytes={……};
      blob.setBytes(1,bytes,0,512);
    
      除了可以向Blob对象增加字节之外,Blob接口还提供了删除字节的方法。方法truncate接受一个字节数目作为一个参数并且根据这个数目来缩短Blob对象。
  6. 定位器和更新
      在标准实现中,指向SQL BLOB的Blob对象使用了SQL LOCATOR类型。定位器(locator)是一个指向保存在数据库中的BLOB值的指针,而DBMS如何更新一个作为定位器实现的对象则依赖于具体的数据库。某些DBMS会更新表中的BLOB值,而另一些则仅仅更新BLOB值的一个副本,并不改变数据库中的值。在后一种情况下,应用程序必须直接更新BLOb值。
      为了发现DBMS是如何更新BLOB值的,应用程序可以调用DatabaseMetaData的方法locatorsUpdateCopy。如果这个方法返回true,则应用程序必须自己更新数据库中的BLOB值。下面的代码显示了这个过程:首先从rs取回Blob对象,然后把它的值改为字节数据val的值。如果方法locatorsUpdateCopy返回true,那么它随后执行一个PreparedStatement对象来更新数据库中的值。如果方法locatorsUpdateCopy返回false,代码什么也不用做,因为数据库中的值已经被更新过了。
    byte [] val ={0,1,2,3,4};
      Blob data =rs.getBlob(“DATA”);
      int numWritten = data.setBytes(1,val);
      if (dbmd.locatorUpdateCopy() == true){
       PreparedStatement pstmt= con . preparedStatement(
    “UPDATE statistics SET DATA = ? WHERE REGION = ‘BEIJING' “);
       pstmt.setBlob(“DATA”,data);
       pstmt.executeUpdate();
    }
    

字符大对象Clob

  1. 创建Blob对象
      Clob clob = rs.getClob(1);
      变量clob现在可以被用于在CLOB值上执行操作,而假设这CLOB值保存在结果集rs的第一列中。

  2. 物化Clob数据
      和物化Blob的方式一样。不过Clob接口提供了三种方法达到将CLOB作为一个Java对象的形式保存在客户的内存中。
    使用getAsiiStream把CLOB值物化为一个包含Ascii字节的字符流。
    ① 使用getAsiiStream把CLOB值物化为一个包含Ascii字节的字符流。
    Clob notes = rs.getClob(“NOTES”);
    java.io.InputStream in = notes.getAsciiStream();
    byte b = in.read();
    
    
    ② 使用getCharacterStream把CLOB值物化为一个Unicode字符流。
    Clob notes = rs.getClob(“NOTES”);
    java.io.Reader reader = notes.getCharacterStream();
    int c = reader.read();
    //
    
    
    ③	使用getSubString将CLOB值的全部或者部分化为一个String对象。
    Clob notes = rs.getClob(4);
    String substring= notes.getSubString(10,5);
    或者
    long len =notes.length();
    String substring = notes.getSubString(1,(int)len);
  3. 存储、更新Clob对象
      和存储、更新Blob对象类似。

元数据接口使用详解
  三个元数据接口DatabaseMetaData、ResultSetMetaData和ParameterMetaData接口是三个常用的元数据接口。DatabaseMetaData提供与数据库或者DBMS相关的信息;ResultSetMetaData对象提供与特定ResultSet实例中与列相关的信息;ParameterMetaData对象提供与PreparedStatement对象的参数有关的信息。本文讨论的内容并不仅限于JDBC的某个版本,而是基于1.0—3.0的规范进行学习。

  • ResultSetMetaData对象
      当在JDBC应用程序中发送select语句时,该操作会返回一个ResultSet对象,这个ResultSet对象包含满足条件的数据。通过创建ResultMetaData对象和调用该对象的方法,可以获取与这个ResultSet对象中的列有关的信息。下面的代码段将创建ResultSet对象rs,然后使用rs来创建ResultSetMetaData对象,所创建的ResultSetMetaData对象包含与rs中的列有关的信息。
       Statement stmt= con . createStatement();
       ResultSet rs = stmt.executeQuery(“select * from sales”);
       ResultSetMetaData rsmd = rs.getMetaData();
    
      现在可以使用rsmd调用ResultSetMetaData的方法来访问与rs中的列有关的信息。除了方法getColumnCount给出结果集中总的列数外,所有的ResultSetMetaData方法都返回与单个列有关的信息,并都接受一个表示对应列号的参数。
    1. getColumnCount方法
         这也许是ResultSetMetaData中使用最多的方法,该方法返回结果集中列数目 :
      ResultSet rs = stmt.executeQuery(“select * from sales”);
      ResultSetMetaData rsmd = rs.getMetaData();
      int numberOfColumns = rsmd.getColumnCount();
      while(rs.next()){
       for(int i=1;i<=numberOfColumn;i++){
      String s = rs.getString(i);
      System.out.println(“Column ” +i +”:  ” +s +” ”);
      }
      }
      
        需要注意的是,用于检索所有列值的ResultSet方法是getString。当不知道每一列的类型,这是比较容易的方法;如果希望能检索所有的数据类型(包括SQL 99数据型),则可以使用方法getObject,这是保证能够检索所有列值得唯一方法。
    2. 获取列类型信息
        有两个ResultSetMetaData方法可以获取与结果集列的类型有关的信息。这

      两个方法是getColumnType和getColumnTypeName。getColumnType方法用于确定存储在指定列中的值的JDBC类型。该方法以一个int值来返回JDBC类型。如下面的代码获得rs第二列的JDBC类型:
        ResultSetMetaData rsmd = rs.getMetaData();
        int jdbcType = rsmd.getColumnType(2);
      
    3. 获取其他信息
        另外有几个方法用来提供与存储数值类型的列有关的信息。
      isAutoIncrement
      isCurrency
      isSigned
      getPrecision
      getScale
      isNullable
      getColumnDisplaySize
    1. 使用DatabaseMetaData对象
        接口DatabaseMetaData提供了大量的方法取得与数据库相关的信息。一旦获得了打开的连接,就可以创建包含与数据库系统有关的信息的DatabaseMetaData对象。
    2. DatabaseMetaData方法的类别
        按照返回值的类型对DatabaseMetaData的方法进行分类,可以分为4种。
        有三种类型返回单一的值,另外一种返回一个结果集,这个结果集包含1~18列的数据。 ① 返回String的方法
        最小的类别是指返回String对象的DatabaseMetaData方法。这些方法中的一些方法可以获取与DBMS有关的总体信息,包括数据库的URL、username、产品名称、驱动程序信息等等。
      ② 返回int的方法
      ③ 返回boolean的方法
      ④返回ResultSet对象的方法
        这些方法可以返回ResultSet对象,所返回的ResultSet对象可以包含1到最多18列。
    3. 获取与主外键有关的信息
        返回与主外键有关信息的方法主要有getPrimaryKeys、getImportedKeys、getExportedKeys以及getCrossReference等。
        下面的代码段显示了如果在定义表时指定了主键,则可以调用方法getPrimaryKeys开获取对于表中主键列的描述。
       import java.sql.*;
         public class static PrimaryKeysExample{
        public static void main(String args[]){
      String url =”jdbc:mySubprotocol:myDataSource”;
      Connection con;
      String createString =”create table supplierspk”+
               “(sup_id integer not null,”+
               “sup_name varchar(40),”+
               “street varchar(40),”+
               “city varchar(20)”+
               “state char(10),”+
               “zip char(10),”+
               “primary key(sup_id))”;
      Statement stmt;
      try{
       Class.forName(“myDriver.className”);
      }catch(java.lang.ClassNotFoundException e){
       System.err.println(“ClassNotFoundException: ”);
       System.err.println(“e.getMessage()”);
      }
      try{
       con =DriverManager.getConnection(url,”username”,”pwd”);
       stmt=con.createStatement;
       stmt.executeUpdate(createString);
       DatabaseMetaData dbmd=con.getMetaData();
       ResultSet rs= dbmd.getPrimaryKey(null,null,”suplierspk”);
       While(rs.next()){
       String name =rs.getString(“table_name”);
       String columnName=rs.getString(“column_name”);
       String keySeq=rs.getString(“key_seq”);
       String pkName=rs.getString(“pk_name”);
       System.out.println(“table name :”+name);
       System.out.println(“column name: ”+columnName);
       System.out.println(“sequence in key:”+keySeq);
       System.out.println(“primary key name:”+pkName);
      }
      rs.close();
      stmt.close();
      con.close();
      }catch(SQLException ex){
       System.err.println(“SQLException: ”+ex.getMessage());
      }
      }
      }
      
        如果主键多余一列的话,那么方法getPrimaryKeys等将详细描述每一列。列key_seq中的值表示描述的是哪一列。
  • 使用ParameterMetaData对象

      可以使用ParameterMetaData对象来获取与PreparedStatement对象或者CallableStatement对象有关的信息。这些参数由”?”占位符表示,”?”占位符是提供给Connection方法prepareStatement和prepareCall的SQL语句。下面的代码行使用两个参数占位符来创建一个PreparedStatement对象。
      PreparedStatement pstmt=con.prepareStatement(“select id from employees where dept=? and salary>?”);
      这些参数根据其序号来编号,因此第一个参数编号1,第二个参数编号2,依此类推。在上面的代码行中,参数1是列dept中的一个值,参数2是salary中的一个值。下面的代码段用于找出PreparedStatement pstmt有多少个参数。首先创建pstmt并用它来创建ParameterMetaData对象pmd,这个对象包含与pstmt中的参数有关的信息。接着调用pmd上的方法getColumnCount来找出pstmt有多少参数。

      PreparedStatement pstmt=con.prepareStatement(
    “update employees set salary =? Where level=?”);
      ParameterMetaData pmd = pstmt.getParameterMetaData();
      int count=pmd.getParameterCount();
    
      变量count的值应该等于2。方法getParameterCount不接受参数,因为它返回与PreparedStatement对象的所有参数有关的信息。ParameterMetaData接口中的所有其他方法都接受序号来表示作为要查询的信息的参数。
  • JDBC 3.0API中新增的方法示例
      JDBC 3.0 API引入了这样一种应用功能,即决定在已经调用commit方法来终止事务之后,ResultSet对象是否仍旧打开。这个功能即为结果集可保持性。ResultSet中加入了两个域HOLD_CURSORS_OVER_COMMIT和 CLOSE_CURSORS_AT_COMMIT,这些域可以用于指定ResultSet对象的可保持性,这些常量可以提供给创建Statement、PreparedStatement和CallableStatement对象的Connection方法,还可以作为提供给DataBaseMetaData方法supportResultSetHoldability的可能的参数值。


      在下面的代码段中,首先判断驱动程序是否支持ResultSet可保持性,如果支持,则接着创建会生成带有可保持性游标的ResultSet对象的Statement对象,另外此代码还指定了stmt在执行查询时产生的ResultSet对象的类型和并发模式。

    if (Boolean b =dbms.supportsResultSetHoldability(
    ResultSet. HOLD_CURSORS_OVER_COMMIT){
     Statement.stmt=con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
    ResultSet.CONCUR_UPDATABLE,
    ResultSet.HOLD_CURSORS_OVER_COMMIT);
    }
    
      参考《JDBC API Reference》 Maydene Fisher etc. 著

RowSet
  RowSet对象是表格式数据的容器,封装了一组从数据源获取得数据行。在RowSet接口的基本实现中是从JDBC数据源中获取数据行。由于行集是可以定制的,所以行集中的数据可以来自电子数据表、flat文件(平面文件?)或者其他任何表格式样的数据源。RowSet对象是ResultSet接口的扩展,这就意味着RowSet对象是可滚动的、可更新的,并且能够执行ResultSet对象可以执行的任何操作。

  RowSet对象不同于ResultSet对象,它是JavaBean组件,因此,该对象有JavaBean属性,并遵循其事件模型。另外RowSet对象的属性也允许该对象建立自己的数据库连接,执行自己的查询。此外,RowSet可以是disconnected的,也就是说使用行集的过程中,不必一直保持到数据源的打开连接。另外,行集可以串行化,所以可以通过网络把它发送到远程对象中去。

  通常情况下,JDBC API可以分为两类:RowSet部分和驱动程序部分。RowSet以及支持它的接口使用其他JDBC API实现。从逻辑上来说,可以把实现RowSet接口的类看作是执行在JDBC驱动程序的上一层的软件。

  现在的J2SE 5.0中,可以把JDBC API分为三种类别,除了上面的两类,还有RowSet接口的5种标准实现。这些实现提供了一组接口,以扩展基本的RowSet接口,通过在这些接口上建立application,可以确保在事件处理、游标控制以及其他操作方面的实现遵循JDBC API。

  RowSet接口提供了一组基本方法,这些方法对于所有行集都是通用的。因为所有的RowSet对象都是JavaBean组件,因此,RowSet接口具有添加和删除event listener的方法,也有get/set RowSet对象所有属性的方法。RowSet对象的大多数属性都支持建立连接或者执行命令。为了执行查询、更新等SQL语句,并生成能够从中获取数据的结果集,行集会使用到数据源的连接。

  若一个组件希望得到在RowSet对象上发生的事件的通知,它就应该实现RowSetListener接口,并向RowSet对象进行注册。这就是listener,一个GUI组件。这样每当行集产生事件时,listener每次都会得到事件的通知,这样就能够保持它的游标位置和数据与行集的内容一致。

  RowSetInternal、RowSetReader、和RowSetWriter接口支持行集的reader/weiter工具。reader是一个实现RowSetReader接口的类的实例,用来读取数据并数据插入到行集中。writer是一个实现RowSetWriter接口的类的实例,用来将修改后的数据写回到数据源。reader和writer就像是listener一样,都向行集进行动态注册。

  调用reader或者writer的RowSet对象必须是实现RowSetInternal接口的类的实例。这个接口为reader或者writer提供了附加的方法,用来操作行集的内部状态。例如行集可以跟踪它的初始值,RowSetInternal方法允许writer检测数据源中的相应数据是否已经被其他用户修改。另外,能够使用RowSetInternal方法来获取为行集的命令字符串设置的输入参数,也能够获取传递给行集的连接。最后,RowSetInternal方法允许reader设置新的RowSetMetaData对象,这个对象用来为行集描述reder将要插入到该行集中的数据行。

  行集可以是connected的,也可以是disconnected的,一个连接的RowSet对象,在使用的整个过程中保持到数据源的连接,而一个未连接的行集,只有从数据源读取数据或者将数据写回数据源时才会连接到它的数据源。若行集是未连接的,它就不需要JDBC驱动或者JDBC API的完整实现。这使得它非常小巧,因此也成为发送一组数据到一个thin client的理想的容器。这个客户端可以选择更新数据,并将行集发送回应用服务器。在服务器端,未连接的RowSet对象使用它的reader来建立到数据源的连接,并把数据写回数据源。这一操作的具体实现依赖于reader的实现方式。通常reader将建立连接与读/写数据的操作委托给JDBC驱动。

行集的事件模型
  行集事件模型使得Java对象或者组件能够收到RowSet对象产生的事件的通知。通知机制的建立包括被通知的组件,也包括RowSet对象自身。首先,每一个希望得到事件通知的组件都必须实现RowSetListener接口。然后,RowSet对象必须注册每一个组件,这通过在RowSet对象的事件通知组件列表中添加这些组件来完成。在这种情况下,这样的组件是一个listener,它是一个实现RowSetListener方法的类的实例,并已经向RowSet对象进行过注册。

  在RowSet对象中可能发生三种事件:游标移动、数据行发生变化(ins、del、upd)或者该对象的整个内容发生变化。RowSetListener接口的cursorMoved、rowChanged和rowSetChanged方法分别对应与这些事件。当事件发生时,行集将创建一个RowSetEvent对象,使用这个对象将该行集标识为事件源。在每一个listener上调用适当的RowSetListender方法,并向此方法传递一个RowSetEvent对象作为输入参数,就将事件通知给行集所有的listener。

RowSet属性
  RowSet接口提供了一组JavaBeans属性,可以配置RowSet实例以连接到数据源并获取数据行集合。有些属性可能并不需要,这取决于特定实现。如用URL或者数据源名来建立连接,但只要设置了其中一个属性,另一个属性就是可选的。如果设置了两个属性,就会使用最近设置的那个属性。若行集的数据是从不支持名命令的非SQL数据源中如电子表格中获取的,那么就不需要设置命令属性。

几个接口的讲解

  1. WebRowSet
      WebRowSet接口扩展了CacheRowSet接口。相比CacheRowSet接口,增加了读写XML格式行集的能力。WebRowSetImpl对象使用WebRowSetXmlReader对象来读取XML格式的行集,使用WebRowSetXmlWriter对象向XML格式的行集中写入数据。XML版本包含WebRowSetSetImpl对象的元数据,同时也包含它自己的数据。   WebRowSetImpl对象和CachedRowSetImpl相似点是二者都将瘦客户端连接到应用服务器。所以它们都适合为瘦客户提供数据;不同的地方是二者使用的协议。前者使用http/xml协议与中间层进行通信,而后者使用RMI/IIOP。  
  2. JoinRowSet
      JoinRowSet对象使得程序员能够从两个不同的RowSet对象合并数据。当相关联(p/f keys相关或者其他列唯一对应)的数据存储在不同的数据源中时,这种合并数据的功能很有用。任何RowSet实现都可以参与合并,但通常情况下,连接的双方是两个CachedRowSetImpl对象。将相关数据合并到一个JoinRowSetImpl对象中,应用程序就能够处理任何其他类型的RowSet对象一样来处理这些数据。
       假设有两个表:employees表和bonus_plan表,两表的第一个数据列都是ID,这个数据列是主键,现要将两表的信息进行匹配合并:
      JoinRowSetImpl jrs=new JoinRowSetImpl();
      ResultSet rs1 = stmt.executeQuery(“select * from employees”);
      CachedRowSetImpl empl=new CachedRowSetImpl();
      empl.populate(rs1);
      empl.setMatchColumn(1);
      jrs.addRowSet(empl);
      ResultSet rs2=stmt.executeQuery(“select * from bonum_plan”);
      CachedRowSetImpl bonums= new CachedRowSetImpl();
      bonus.populate(rs2);
      bonus.setMatchColumn(1);
      jrs.addRowSet(bonus);//已合并
    

    XAConnection 介绍
      XAConnection对象是可以用于分布式事务的PooledConnection对象。它表示数据库的物理连接,多层结构中的服务器可以用它来创建返回给应用程序的Connection对象。因为是通过扩展PooledConnection接口得到的,所以继承了其所有的方法,另外添加了自己的方法 getXAResource。
      分布式事务中的命令可以发送给多个DBMS服务器分布式事务是通过中间层应用服务器和外部的事务管理器以及JDBC服务器一同进行管理的,中间层的基础设施的这三个部分提供了plumbing。
      分布式事务基础设施的首要元素是事务管理器,它可以是JTA的具体实现。事务管理器控制着事务边界以及两阶段提交过程。它启动和关闭与分布式事务 XAConnection对象相关的组件,并跟踪那些参与了分布式事务的DBMS服务器。事务管理器在每一个dbms中进行运行决定是否提交事务,仅当所有的dbms都同意提交时,事务管理器才提交事务,否则进行rollback。
      分布式事务的另一个元素是支持jdbc api的jdbc驱动程序,而且这个驱动程序必须包含实现XADataSource和XAConnection接口的类。XADataResource接口和DataSource接口类似,但是它创建的对象是XAConnection对象而不是Connection对象。继承于 PooledConnection的XAConnection对象的特殊在于可以用它来获取XASource对象。事务管理器使用这个XARource对象开始和结束与分布式事务中的这个XAConnection相关的组件。
      分布式事务基础设施中的第三个部分通常是一个连接池模块。XAConnection接口从PooledConnection接口继承而来,这表示分布式事务中的数据库连接可以来自连接池模块所管理的连接池。

    作者简介
    dev2dev ID: lhbing, dev2dev论坛版主,WebLoigc以及Java技术爱好者

« 
» 
快速导航

Copyright © 2016 phpStudy | 豫ICP备2021030365号-3