小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

JDBC MetaData應(yīng)用

 鬼迷心竅 2008-01-06

概述
數(shù)據(jù)庫(kù)程序設(shè)計(jì)通常需要從大量的外部數(shù)據(jù)源中獲取數(shù)據(jù)進(jìn)行處理。這這篇文章中,作
者提出了在數(shù)據(jù)庫(kù)中使用DatabaseMetaData——JDBC源數(shù)據(jù)接口——和執(zhí)行預(yù)編譯的SQ
L實(shí)現(xiàn)將純文本數(shù)據(jù)轉(zhuǎn)換成實(shí)際的數(shù)據(jù)類型的技術(shù)。它提供了一種在運(yùn)行時(shí)動(dòng)態(tài)發(fā)現(xiàn)數(shù)據(jù)
庫(kù)的具體數(shù)據(jù)類型和翻譯外部文本數(shù)據(jù)的方法。
正文
假設(shè)您需要寫一個(gè)將文本文件中的數(shù)據(jù)轉(zhuǎn)換成數(shù)據(jù)庫(kù)的表并進(jìn)行存儲(chǔ)的數(shù)據(jù)庫(kù)應(yīng)用程序
,例如這個(gè)表包含的是航班信息,包括票號(hào)、購(gòu)買日期、出發(fā)日期、離港/到港位置以及
票價(jià)等在數(shù)據(jù)庫(kù)中,每一段信息都有一一個(gè)指定的數(shù)據(jù)類型與之相對(duì)應(yīng)。例如數(shù)值型、
文本型、日期型或者貨幣型等等。
程序必須從文本文件中讀出幾段這樣的票務(wù)信息,將這些信息轉(zhuǎn)換成合適的數(shù)據(jù)類型并
創(chuàng)建一個(gè)相應(yīng)的表以存儲(chǔ)這些轉(zhuǎn)換過的數(shù)據(jù)。為了實(shí)現(xiàn)這種轉(zhuǎn)換,程序需要知道每段數(shù)
據(jù)在數(shù)據(jù)庫(kù)中對(duì)應(yīng)的數(shù)據(jù)類型。簡(jiǎn)單的方法是在程序中通過硬編碼進(jìn)行類型的轉(zhuǎn)換——
將數(shù)據(jù)類型作為編程時(shí)就預(yù)先知道的靜態(tài)信息。
通過采用這種簡(jiǎn)單的方式,您必須要在另外一個(gè)程序中用類似的代碼來實(shí)現(xiàn)同一個(gè)數(shù)據(jù)
庫(kù)中另外一個(gè)表(如具有客戶信息的custumer表)的數(shù)據(jù)生成工作,盡管您可以重復(fù)使
用以前的許多程序代碼,但您必須重新設(shè)計(jì)所有的數(shù)據(jù)庫(kù)數(shù)據(jù)類型轉(zhuǎn)換的代碼。
如果您需要生成的表非常多時(shí),您就會(huì)非常厭倦并且也很浪費(fèi)精力了。但這也僅僅是只
有一個(gè)數(shù)據(jù)庫(kù)的情況。如果將數(shù)據(jù)遷移到其它的數(shù)據(jù)庫(kù)系統(tǒng),您必須重寫所有的代碼以
實(shí)現(xiàn)不同數(shù)據(jù)庫(kù)數(shù)據(jù)類型的對(duì)應(yīng)。在不同的數(shù)據(jù)庫(kù)間實(shí)現(xiàn)數(shù)據(jù)類型的對(duì)應(yīng)和轉(zhuǎn)換在數(shù)據(jù)
庫(kù)編程中一直是一個(gè)非常討厭的事。
創(chuàng)建可重用的代碼
怎樣避免這些重復(fù)的工作呢?您寫代碼時(shí)可以將數(shù)據(jù)庫(kù)名、表名和列名看成參數(shù)。問題
是是不是有辦法對(duì)數(shù)據(jù)類型也可以進(jìn)行類似的處理,這樣您就可以寫一個(gè)類或者一個(gè)類
集完成任何數(shù)據(jù)庫(kù)間任何數(shù)據(jù)類型的對(duì)應(yīng)和表的生成?答案是在運(yùn)行之前不要確定數(shù)據(jù)
類型,在運(yùn)行時(shí)根據(jù)java.sql 包中的DatabaseMetaData接口來實(shí)現(xiàn)它,通過在這個(gè)接口
中寫入代碼,您可以避免對(duì)數(shù)據(jù)類型進(jìn)行硬編碼,這樣就可以寫通用的可重用代碼了。

DatabaseMetaData接口為數(shù)據(jù)庫(kù)提供了元數(shù)據(jù)(metadata)信息。Metadata是描述數(shù)據(jù)
的數(shù)據(jù)。例如,航班數(shù)據(jù)庫(kù)表包含票務(wù)信息,它是數(shù)據(jù)。在這種情況下,元數(shù)據(jù)會(huì)包含
諸如表中有多少列、每個(gè)列的數(shù)據(jù)類型、是不是一個(gè)列可以為空等等信息。這就是關(guān)于
數(shù)據(jù)的數(shù)據(jù)本篇關(guān)注的焦點(diǎn)是每個(gè)列的數(shù)據(jù)類型。
在后面的討論中,我會(huì)向您介紹一種以層次方式進(jìn)行組織的三個(gè)java類構(gòu)成的可重用庫(kù)
。每層都對(duì)下層進(jìn)行了封裝,最下層離數(shù)據(jù)庫(kù)最近。相應(yīng)地,最上層離離應(yīng)用程序最近
了。雖然這些庫(kù)是為灌入數(shù)據(jù)庫(kù)的表而設(shè)計(jì)的,但只要您在代碼上進(jìn)行小規(guī)模的改動(dòng),
您就可以建立自己的數(shù)據(jù)類型獨(dú)立的數(shù)據(jù)庫(kù)查詢和更新。
沿用下面的情況
假設(shè)你需要在一個(gè)organization數(shù)據(jù)庫(kù)中灌入一個(gè)稱為emp的表,這里是一個(gè)在第一行包
含表,然后第二行是一系列的列名,隨后的是每行都是相應(yīng)的列對(duì)應(yīng)的數(shù)據(jù)的樣板數(shù)據(jù)
文件:
 emp
 hiredate sal ename empno
  1996-09-01 1250.00 jackson 7123
  1980-01-01 2500.50 walsh 7124
  1985-01-01 12345.67 gates 7125
所有的輸入是純ASCII文本,但這些數(shù)據(jù)會(huì)轉(zhuǎn)換成如下的數(shù)據(jù)庫(kù)特定數(shù)據(jù)類型:
  字段 數(shù)據(jù)庫(kù)類型
  -------- -------------
  hiredate date
  sal decimal
  ename ASCII text
  empno number
emp表的數(shù)據(jù)結(jié)構(gòu)如下圖的頂行所示:
從輸入列到數(shù)據(jù)庫(kù)表列的映射
不是表中所有的列都需要立刻灌入,在這個(gè)例子中,僅僅灌入標(biāo)記為true的列,需要灌
入的列有empno、ename、 hiredate和sal。
在數(shù)據(jù)文件中的列的順序也不需要一定與數(shù)據(jù)庫(kù)中列的順序一致。一個(gè)索引數(shù)組維護(hù)了
文件中的列到數(shù)據(jù)庫(kù)中的列的對(duì)應(yīng)。在這個(gè)例子中,活動(dòng)列(標(biāo)記為true的)的順序數(shù)
組就是索引數(shù)組。如果I是輸入文件中一個(gè)列的位置(hiredate:0、sal:1等等),那
么activeColumnOrder[i]是這些列在表中的相應(yīng)位置。(activeColumn[0]是5,意思是
說hiredate是emp表中的第五列)。
建立庫(kù)類層次
我們的庫(kù)中包含三個(gè)層次:
一個(gè)TableColumns類,它距離數(shù)據(jù)庫(kù)最近,它負(fù)責(zé)發(fā)現(xiàn)和管理數(shù)據(jù)庫(kù)表列的信息。
一個(gè)TableMediator類,它用來準(zhǔn)備使用TableColumns所管理的數(shù)據(jù)庫(kù)表信息對(duì)表進(jìn)行灌
入。
一個(gè)TableBuilder類,它離數(shù)據(jù)庫(kù)最遠(yuǎn),當(dāng)然離應(yīng)用程序最近。它負(fù)責(zé)從輸入的數(shù)據(jù)文
件中讀取數(shù)據(jù)然后使用TableMediator類將數(shù)據(jù)灌入指定的表。
下圖說明這種層次關(guān)系:
應(yīng)用分層
這些類都收集在稱為tablebuild的包中。
這些Java源程序可以在這里(http://www./javatips/javatip82/tablebuild.zip
第一層:TableColumns類
TableColumns類負(fù)責(zé)通過查詢給定數(shù)據(jù)庫(kù)的元數(shù)據(jù)實(shí)例發(fā)現(xiàn)數(shù)據(jù)庫(kù)表的給定列信息。它
將列信息存儲(chǔ)在兩個(gè)并列的數(shù)組中:一個(gè)稱為columnNames的字符串?dāng)?shù)組,一個(gè)稱為col
umnTypeCodes的短整型數(shù)組。
一個(gè)給定表的所有列的類型可以通過DatabaseMetaData的getColumns方法獲得:
    public abstract ResultSet getColumns(String catalog,
                                  String schemaPattern,
                                  String tableNamePattern,
                                  String columnNamePattern)
        throws SQLException
getColumns方法需要四個(gè)參數(shù):一個(gè)類目名(catalog name)、一個(gè)方案名(schema n
ame)、一個(gè)表名(table name)和一個(gè)列名(column name)。它們中,最后三個(gè)帶有
Pattern后綴的(如上面的代碼)很有意思,因?yàn)樗鼈冊(cè)试S你搜索所有的匹配模式表達(dá)式
的目標(biāo)字符串,也就是說,您通過這些參數(shù)指定查詢準(zhǔn)則。如下面所指出的:
尋找所有的滿足下面的準(zhǔn)則的列:它們屬于一個(gè)給定的類目、并且屬于匹配給定方案模
式的任何一個(gè)方案、并且匹配給定表名模式的任何一個(gè)表。當(dāng)然,它們的名字也要匹配
給定的列名模式。
模式使用類似SQL語(yǔ)句的語(yǔ)法進(jìn)行指定,它通過LIKE從句指定匹配名。獨(dú)特的地方是,它
通過在模式表達(dá)式中使用下劃線以匹配目標(biāo)字符串中的任何字符,通過百分號(hào)匹配目標(biāo)
字符串中任何數(shù)量的連續(xù)字符。例如,模式表達(dá)式j(luò)_b會(huì)匹配"job"和"jab",而模式表達(dá)
式j(luò)%b會(huì)匹配任何以"j"開始,以"b"結(jié)束中間包含任意數(shù)量(包括0)字符的目標(biāo)字符串
。
下面是在TableColumns 構(gòu)造方法中對(duì)getColumns方法的調(diào)用,其中dbMeta是給定數(shù)據(jù)庫(kù)
的DatabaseMetaData的一個(gè)實(shí)例。
    ResultSet rset = dbMeta.getColumns(null, null, table.toUpperCase(), "%")
;
注意,catalog名和schema模式參數(shù)的值是null??罩当硎具@個(gè)參數(shù)可以從搜索準(zhǔn)則中省
略。另外,要注意我們將指定表名的大寫傳給表名模式。還不清楚這是JDBC的需要還是
JDBC驅(qū)動(dòng)器的偏好。我在發(fā)現(xiàn)大寫的表名能夠讓代碼工作以前,花了很多時(shí)間去檢查我
的代碼哪里出了問題。最后,"%"分配給列名模式以獲得所有的列。它可以解釋為“取得
給定表的所有列”。
GetColumns方法返回的java.sql.ResultSet結(jié)果集為每一個(gè)匹配的列返回一行。每行包
括18個(gè)描述域,這就是結(jié)果集列。與我們的類相關(guān)的域包括列名、列類型碼,它們是列
的第4和5個(gè)域。列類型碼是java.sql.Types 類中表示列中SQL類型的一個(gè)常量

在這個(gè)例子中,通過表名對(duì)emp的getColumns調(diào)用會(huì)返回所有8個(gè)字段的一個(gè)描述。在結(jié)
果集中每行一個(gè)。列的類型碼已經(jīng)被取出并且存儲(chǔ)在稱為columnTypeCodes的類型數(shù)組中
,這樣就可以被TableMediator類所調(diào)用。我們會(huì)在下面檢查這個(gè)過程。
第二層:TableMediator類
TableMediator類封裝了下面的功能:
創(chuàng)建一個(gè)預(yù)備語(yǔ)句(一個(gè)帶有參數(shù)的預(yù)編譯SQL語(yǔ)句,這些參數(shù)可以在運(yùn)行時(shí)提供)以灌
入表行。語(yǔ)句的參數(shù)對(duì)應(yīng)活動(dòng)列的值。
為一個(gè)活動(dòng)列的集合執(zhí)行這個(gè)預(yù)備語(yǔ)句。
TableMediator類使用TableColumns類以獲得給定表的完全列信息,然后將這些信息與給
定的活動(dòng)列集進(jìn)行比較以實(shí)現(xiàn)前面第一個(gè)圖中說明的配置。
PrepareRowInserts方法創(chuàng)建所需的預(yù)備語(yǔ)句。在這個(gè)例子中,預(yù)備語(yǔ)句象下面的樣子:

    insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
               values (?, ?, ?, ?, ?, ?, ?, ?)
讓您的庫(kù)可移植的第一個(gè)關(guān)鍵因素是使用DatabaseMetaData接口來發(fā)現(xiàn)列的類型。如在
TableColumns類中所看到的。第二個(gè)是使用通用的java.sql.PreparedStatement 接口的
setObject方法:
    ps.setObject(activeColumnOrder[i], columnValues[i]);
這行代碼出現(xiàn)在TableMediator類的insertRow方法中,ps是先前創(chuàng)建的PreparedStatem
ent實(shí)例。
SetObject方法需要兩個(gè)參數(shù):第一個(gè)是與之相聯(lián)系的預(yù)備語(yǔ)句中的參數(shù)的位置;第二個(gè)
是這個(gè)參數(shù)的值。通常,第二個(gè)參數(shù)可以是任何類型的對(duì)象;這段代碼中是字符串。JD
BC驅(qū)動(dòng)器承擔(dān)將這個(gè)字符串轉(zhuǎn)換成合適的數(shù)據(jù)庫(kù)類型的責(zé)任。

在這個(gè)語(yǔ)句中,我們沒有使用事先發(fā)現(xiàn)的列類型。然而,我們確實(shí)需要值為空的列類型
——例如要向數(shù)據(jù)庫(kù)中灌入沒有提供任何值的列。在這種情形,我們會(huì)在PreparedStat
ement接口中使用一個(gè)setNull方法。這里是在TableMediator類的insertRow方法中使用
它的方法:
    ps.setNull(i, tc.columnTypeCodes[i]);
這個(gè)語(yǔ)句將預(yù)備語(yǔ)句參數(shù)I的值設(shè)置為空,但這個(gè)設(shè)置需要附帶傳遞對(duì)應(yīng)列的數(shù)據(jù)類型作
為第二個(gè)參數(shù)。
第三層:TableBuilder類
一旦建立了TableColumns 和TableMediator類,使用TableBuilder類將輸入數(shù)據(jù)灌入表
的代碼就很簡(jiǎn)單明了了。
TableBuilder封裝了下面的功能:
從文本文件中讀取第一行以確定表名并創(chuàng)建一個(gè)TableMediator類的實(shí)例。
從文本文件的第二行讀取列名,并要求TableMediator實(shí)例為這些列設(shè)置活動(dòng)列列表。
要求TableMediator實(shí)例準(zhǔn)備將列插入列表。
從文本文件的隨后的行中讀取列數(shù)據(jù)并要求TableMediator實(shí)例將這些數(shù)據(jù)插入表的下一
行。
使用tablebuild庫(kù)
給定tablebuild庫(kù),很容易寫一個(gè)可以灌數(shù)據(jù)庫(kù)表的應(yīng)用程序。下面是一個(gè)應(yīng)用實(shí)例:

    import java.sql.*;
    import java.io.*;
    import tablebuild.*;
    public class PopulateTable
        {
        public static final String usage = "usage: java PopulateTable " +
                       "";
        public static void main(String[] args)
        throws SQLException, ClassNotFoundException, IOException
        {
            if (args.length != 1) {
                System.err.println(usage);
                System.exit(1);
            }
            Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
            Connection conn = DriverManager.getConnection(
                        "jdbc:odbc:orgdb");
            BufferedReader br = new BufferedReader(
                        new FileReader(args[0]));
            // tab-delimited input data file
            TableBuilder tb = new TableBuilder(conn, br, " ");
            tb.buildTableInfo();
            tb.buildActiveColumns();
            tb.buildTable();
        }
        }
這個(gè)例子使用JDBC-ODBC橋驅(qū)動(dòng)Microsoft Access數(shù)據(jù)庫(kù),數(shù)據(jù)源被命名為orgdb。我主
要選擇這個(gè)橋來說明是因?yàn)樗峁┝颂峁┐蟛糠諮DBC能力的參考實(shí)現(xiàn)。
為了完成這個(gè)例子,您可以通過上面的應(yīng)用程序向orgdb數(shù)據(jù)源的emp表灌入到數(shù)據(jù)。如
果輸入文件名是empfile,您可以這樣運(yùn)行這個(gè)應(yīng)用程序:
 java PopulateTable empfile
這樣就可以將給定數(shù)據(jù)灌入到表的hiredate、sal、ename和empno列。并將輸入數(shù)據(jù)的值
轉(zhuǎn)換成所需的數(shù)據(jù)庫(kù)指定數(shù)據(jù)類型。
如果您有一個(gè)新的數(shù)據(jù)源,您不需要修改應(yīng)用程序和tablebuild類。只要維護(hù)相同的輸
入格式。也就是說,第一行包含表名,第二行包含列名,隨后的行包含一些行的所有列
數(shù)據(jù),列的分隔符可以是任何東西,由于它是可以在TableBuilder類中通過參數(shù)進(jìn)行設(shè)
置的。
測(cè)試您的JDBC驅(qū)動(dòng)程序
本文中,您已經(jīng)知道了創(chuàng)建一個(gè)數(shù)據(jù)類型獨(dú)立的類庫(kù)向數(shù)據(jù)庫(kù)的表中灌入列數(shù)據(jù)的方法
。其關(guān)鍵是將所有的數(shù)據(jù)類型的管理工作留給JDBC的java.sql.DatabaseMetaData和jav
a.sql.PreparedStatement接口的驅(qū)動(dòng)器實(shí)現(xiàn)。您可以使用本文中的說明方法完成數(shù)據(jù)庫(kù)
的插入操作,當(dāng)然也可以實(shí)現(xiàn)數(shù)據(jù)庫(kù)查詢和更新。
一個(gè)重要的警告:動(dòng)態(tài)類型發(fā)現(xiàn)和類型轉(zhuǎn)換的能力最終依賴于您所使用的JDBC驅(qū)動(dòng)器的
實(shí)現(xiàn)。例如,有些JDBC驅(qū)動(dòng)器可能不支持PreparedStatement的setObject方法的類型轉(zhuǎn)
換能力。另外,不同的JDBC驅(qū)動(dòng)器對(duì)DatabaseMetaData接口的實(shí)現(xiàn)是不同的。在JDBC編
程中的通用規(guī)則是,當(dāng)沒有實(shí)現(xiàn)JDBC規(guī)范中所允許的處理能力時(shí),就該懷疑JDBC驅(qū)動(dòng)程
序了。大多數(shù)情況下,一個(gè)好的驅(qū)動(dòng)器一定兼容JDBC規(guī)范,所以也就一定具有這個(gè)功能
。<

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多