<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    javaGrowing

      BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
      92 隨筆 :: 33 文章 :: 49 評論 :: 0 Trackbacks
    JDBC基礎(chǔ)(一)

    本來不想寫這部份入門級的內(nèi)容,但既然欄目定為JDBC專欄,還是簡單寫一些吧.
    JDBC基礎(chǔ)(一)

        來,我們認識一下!
        JDBC,JAVA平臺的DATABASE的連通性.白話一句,什么意思啊?
        就是JAVA平臺上和數(shù)據(jù)庫進行連結(jié)的"工具".

        還是先一起來回顧一下接口吧:從下向上,接口是對"案例"的抽象,由一個案例抽象出一些規(guī)則.
    反過來,從上向下,被抽象出來的接口是對案例的一種承諾和約束.
        也就是說,只要你實現(xiàn)我規(guī)定的接口,你的類就已經(jīng)具有了接口對外承諾的方法,只要"客戶"會
    操作接口,不需要重新學(xué)習(xí)就會操作實現(xiàn)了該接口的新類!
        好了,用行話來說:
        1.通過接口可以實現(xiàn)不相關(guān)的類的相同行為.
        2.通過接口可以指明多個類需要實現(xiàn)的方法.
        3.通過接口可以了解對象的交互方法而不需要了解對象所對應(yīng)的類藍本.
        這幾句話很明白吧?好象有一本什么模式的書把這段話用了30多頁寫出來,結(jié)果別人看了還不如
    我這幾句話明白,不過我明白了為什么有些人要寫書了.

        搞懂了以上這東西,JDBC就好明白了.
        為了通用,JAVA中要求有一種機制,在操作不同廠商數(shù)據(jù)庫時有相同的方法去操作,而不是每接
    觸一種數(shù)據(jù)庫就要學(xué)習(xí)新的方法.完成這種機制的"東西"就叫"JDBC"了.
        簡單地分,JDBC有兩部分組成,JDBC API和JDBC Driver Interface.
        JDBC API就是提供給"客戶"(就是象你我這種菜鳥級程序員來用的,如果是高手都自己寫JDBC了,
    哈哈)的一組獨立于數(shù)據(jù)庫的API,對任何數(shù)據(jù)庫的操作,都可以用這組API來進行.那么要把這些通用的API
    翻譯成特定數(shù)據(jù)庫能懂的"指令",就要由JDBC Driver Interface來實現(xiàn)了,所以這部份是面向JDBC驅(qū)動程
    序開發(fā)商的編程接口,它會把我們通過JDBC API發(fā)給數(shù)據(jù)庫的通用指令翻譯給他們自己的數(shù)據(jù)庫.


        還是通過實際操作來看看JDBC如何工作的吧.

        因為JDBC API是通用接口,那么程序是如何知道我要連結(jié)的是哪種數(shù)據(jù)庫呢?所以在和數(shù)據(jù)庫連
    結(jié)時先要加載(或注冊可用的Driver),其實就是JDBC簽名.加載驅(qū)動程序和好多方法,最常用的就是先把驅(qū)
    動程序類溶解到內(nèi)存中,作為"當(dāng)前"驅(qū)動程序.注意"當(dāng)前"是說內(nèi)存中可以有多個驅(qū)動程序,但只有現(xiàn)在加
    載的這個作為首選連結(jié)的驅(qū)動程序.
        Class.forName("org.gjt.mm.mysql.Driver");
        Class.forName方法是先在內(nèi)存中溶解簽名為"org.gjt.mm.mysql.Driver"的Driver類,Driver類
    就會把相應(yīng)的實現(xiàn)類對應(yīng)到JDBC API的接口中.比如把org.gjt.mm.mysql.Connection的實例對象賦給
    java.sql.Connection接口句柄,以便"客戶"能通過操作java.sql.Connection句柄來調(diào)用實際的
    org.gjt.mm.mysql.Connection中的方法.之于它們是如果映射的,這是廠商編程的,"客戶"只要調(diào)用
    Class.forName("org.gjt.mm.mysql.Driver");方法就可以順利地操作JDBC API了.

        一個普通數(shù)據(jù)庫的連結(jié)過程為:
        1.加載驅(qū)動程序.
        2.通過DriverManager到得一個與數(shù)據(jù)庫連結(jié)的句柄.
        3.通過連結(jié)句柄綁定要執(zhí)行的語句.
        4.接收執(zhí)行結(jié)果.
        5.可選的對結(jié)果的處理.
        6.必要的關(guān)閉和數(shù)據(jù)庫的連結(jié).
    JDBC基礎(chǔ)(二)

    因為是基礎(chǔ)篇,所以還是對每一步驟簡單說明一下吧:

        前面說是,注冊驅(qū)動程序有多方法,Class.forName();是一種顯式地加載.當(dāng)一個驅(qū)
    動程序類被Classloader裝載后,在溶解的過程中,DriverManager會注冊這個驅(qū)動類的實例.
    這個調(diào)用是自動發(fā)生的,也就是說DriverManager.registerDriver()方法被自動調(diào)用了,當(dāng)然
    我們也可以直接調(diào)用DriverManager.registerDriver()來注冊驅(qū)動程序,但是,以我的經(jīng)驗.
    MS的瀏覽中APPLET在調(diào)用這個方法時不能成功,也就是說MS在瀏覽器中內(nèi)置的JVM對該方法的
    實現(xiàn)是無效的.
        另外我們還可以利用系統(tǒng)屬性jdbc.drivers來加載多個驅(qū)動程序:
    System.setProperty("jdbc.drivers","driver1:driver2:.....:drivern");多個驅(qū)動程序之
    間用":"隔開,這樣在連結(jié)時JDBC會按順序搜索,直到找到第一個能成功連結(jié)指定的URL的驅(qū)動
    程序.
        在基礎(chǔ)篇里我們先不介紹DataSource這些高級特性.

        在成功注冊驅(qū)動程序后,我們就可以用DriverManager的靜態(tài)方法getConnection來得
    到和數(shù)據(jù)庫連結(jié)的引用:
        Connection conn = DriverManager.getConnection(url);
        如果連結(jié)是成功的,則返回Connection對象conn,如果為null或拋出異常,則說明沒有
    和數(shù)據(jù)庫建立連結(jié).
        對于getConnection()方法有三個重載的方法,一種是最簡單的只給出數(shù)據(jù)源即:
    getConnection(url),另一種是同時給出一些數(shù)據(jù)源信息即getConnection(url,Properties),
    另外一種就是給出數(shù)據(jù)源,用戶名和密碼:getConnection(url,user,passwod),對于數(shù)據(jù)源信息.
    如果我們想在連結(jié)時給出更多的信息可以把這些信息壓入到一個Properties,當(dāng)然可以直接壓
    入用戶名密碼,別外還可以壓入指定字符集,編碼方式或默認操作等一些其它信息.
        
        在得到一個連結(jié)后,也就是有了和數(shù)據(jù)庫找交道的通道.我們就可以做我們想要的操
    作了.
        還是先來介紹一些一般性的操作:
        如果我們要對數(shù)據(jù)庫中的表進行操作,要先緣故綁定一個語句:
        Statement stmt = conn.createStatement();
        然后利用這個語句來執(zhí)行操作.根本操作目的,可以有兩種結(jié)果返回,如果執(zhí)行的查詢
    操作,返回為結(jié)果集ResultSet,如果執(zhí)行更新操作,則返回操作的記錄數(shù)int.
        注意,SQL操作嚴(yán)格區(qū)分只有兩個,一種就是讀操作(查詢操作),另一種就是寫操作(更
    新操作),所以,create,insert,update,drop,delete等對數(shù)據(jù)有改寫行為的操作都是更新操作.

        ResultSet rs = stmt.executeQuery("select * from table where xxxxx");
        int x = stmt.executeUpdate("delete from table where ......");
        如果你硬要用executeQuery執(zhí)行一個更新操作是可以的,但不要把它賦給一個句柄,
    當(dāng)然稍微有些經(jīng)驗的程序員是不會這么做的.
        至于對結(jié)果集的處理,我們放在下一節(jié)討論,因為它是可操作的可選項,只有查詢操作
    才返回結(jié)果集,對于一次操作過程的完成,一個非常必要的步驟是關(guān)閉數(shù)據(jù)庫連結(jié),在你沒有了
    解更多的JDBC知識這前,你先把這一步驟作為JDBC操作中最最重要的一步,在以后的介紹中我會
    不斷地提醒你去關(guān)閉數(shù)據(jù)庫連結(jié)!!!!!!!!!!!

        按上面介紹的步驟,一個完成的例子是這樣的:(注意,為了按上面的步驟介紹,這個例
    子不是最好的)
        try{
            Class.forName("org.gjt.mm.mysql.Driver");
        }catch(Exception e){
            System.out.println("沒有成功加載驅(qū)動程序:"+e.toString());
            return;
        }//對于象我這樣的經(jīng)驗,可以直接從e.toString()的簡單的幾個字判斷出異常原因,
         //如果你是一個新手應(yīng)該選捕獲它的子類,如何知道要捕獲哪幾個異常呢?一個簡單
         //的方法就是先不加try{},直接Class.forName("org.gjt.mm.mysql.Driver");,編
         //譯器就會告訴你要你捕獲哪幾個異常了,當(dāng)然這是偷機取巧的方法,最好還是自己
         //去看JDK文檔,它會告訴你每個方法有哪些異常要你捕獲.
        Connection conn = null;
        try{
            conn = DriverManager.getConnection(
                            "jdbc:mysql://host:3306/mysql",
                            "user",
                            "passwd");
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery("select * from table");
            //rs 處理
            [rs.close();]
            [stmt.close();]
        }
        catch(Exception e){
            System.out.println("數(shù)據(jù)庫操作出現(xiàn)異常:"+e.toString());
        }
        finally{
            try{conn.close();}catch(Exception){}
        }//不管你以前是學(xué)習(xí)到的關(guān)于數(shù)據(jù)庫流程是如何操作的,如果你相信我,從現(xiàn)在開始,
         //請你一定要把數(shù)據(jù)庫關(guān)閉的代碼寫到finally塊中,切切!
    JDBC基礎(chǔ)(三)

    關(guān)于Statement對象:
        前面說過,Statement對象是用來綁定要執(zhí)行的操作的,在它上面有三種執(zhí)行方法:
    即用來執(zhí)行查詢操作的executeQuery(),用來執(zhí)行更新操作的executeUpdate()和用來執(zhí)行
    動態(tài)的未知的操作的execute().
        JDBC在編譯時并不對要執(zhí)行的SQL語句檢測,只是把它看著一個String,只有在驅(qū)動
    程序執(zhí)行SQL語句時才知道正確與否.
        一個Statement對象同時只能有一個結(jié)果集在活動.這是寬容性的,就是說即使沒有
    調(diào)用ResultSet的close()方法,只要打開第二個結(jié)果集就隱含著對上一個結(jié)果集的關(guān)閉.所以
    如果你想同時對多個結(jié)果集操作,就要創(chuàng)建多個Statement對象,如果不需要同時操作,那么可
    以在一個Statement對象上須序操作多個結(jié)果集.
        
        這里我不得不特別說明一下,很多人會用一個Statement進行嵌套查詢,然后就來問
    我說為什么不能循環(huán)?道理上面已經(jīng)說清楚了.我們來詳細分析一下嵌套查詢:
        Connection conn = null;
        Statement stmt = null;
        conn = .......;
        stmt = conm.createStatement(xxxxxx);
        ResultSet rs = stmt.executeQuery(sql1);
        while(rs.next()){
            str = rs.getString(xxxxx);
            ResultSet rs1 = stmt.executeQuery("select * from 表 where 字段=str");
        }
    當(dāng)stmt.executeQuery("select * from 表 where 字段=str");賦給rs1時,這時隱含的操作
    是已經(jīng)關(guān)閉了rs,你還能循環(huán)下去嗎?
    所以如果要同時操作多個結(jié)果集一定要讓它他綁定到不同的Statement對象上.好在一個connection
    對象可以創(chuàng)建任意多個Statement對象,而不需要你重新獲取連結(jié).

        關(guān)于獲取和設(shè)置Statement的選項:只要看看它的getXXX方法和setXXX方法就明白了,這兒
    作為基礎(chǔ)知識只提一下以下幾個:
        setQueryTimeout,設(shè)置一個SQL執(zhí)行的超時限制.
        setMaxRows,設(shè)置結(jié)果集能容納的行數(shù).
        setEscapeProcessing,如果參數(shù)為true,則驅(qū)動程序在把SQL語句發(fā)給數(shù)據(jù)庫前進行轉(zhuǎn)義替
    換,否則讓數(shù)據(jù)庫自己處理,當(dāng)然這些默認值都可以通過get方法查詢.

        Statement的兩個子類:
        PreparedStatement:對于同一條語句的多次執(zhí)行,Statement每次都要把SQL語句發(fā)送給數(shù)據(jù)
    庫,這樣做效率明顯不高,而如果數(shù)據(jù)庫支持預(yù)編譯,PreparedStatement可以先把要執(zhí)行的語句一次發(fā)
    給它,然后每次執(zhí)行而不必發(fā)送相同的語句,效率當(dāng)然提高,當(dāng)然如果數(shù)據(jù)庫不支持預(yù)編譯,
    PreparedStatement會象Statement一樣工作,只是效率不高而不需要用戶工手干預(yù).
        另外PreparedStatement還支持接收參數(shù).在預(yù)編譯后只要傳輸不同的參數(shù)就可以執(zhí)行,大大
    提高了性能.
            
        PreparedStatement ps = conn.prepareStatement("select * from 表 where 字段=?");
        ps.setString(1,參數(shù));
        ResultSet rs = ps.executeQuery();
        
        CallableStatement:是PreparedStatement的子類,它只是用來執(zhí)行存儲過程的.
        CallableStatement sc = conn.prepareCall("{call query()}");
        ResultSet rs = cs.executeQuery();
        
        關(guān)于更高級的知識我們在JDBC高級應(yīng)用中介紹.
    JDBC基礎(chǔ)(四)

        作為基礎(chǔ)知識的最后部分,我們來說一說結(jié)果集的處理,當(dāng)然是說對一般結(jié)果集的處理.
    至于存儲過程返回的多結(jié)果集,我們?nèi)匀环旁诟呒墤?yīng)用中介紹.
        SQL語句如何執(zhí)行的是查詢操作,那就要返回一個ResultSet對象,要想把查詢結(jié)果最后
    明白地顯示給用戶,必須對ResultSet進行處理.ResultSet返回的是一個表中符合條件的記錄,對
    ResultSet的處理要逐行處理,而對于每一行的列的處理,則可以按任意順序(注意,這只是JDBC規(guī)
    范的要求,有些JDBC實現(xiàn)時對于列的處理仍然要求用戶按順序處理,但這是極少數(shù)的).事實上,雖
    然你可以在處理列的時候可以按任意順序,但如果你按從左到右的順序則可以得到較高的性能.

        這兒從底層來講解一下ResultSet對象,在任何介紹JDBC的書上你是不會獲得這樣的知
    識的,因為那是數(shù)據(jù)庫廠商的事.ResultSet對象實際維護的是一個二維指針,第一維是指向當(dāng)前
    行,最初它指向的是結(jié)果集的第一行之前,所以如果要訪問第一行,就要先next(),以后每一行都
    要先next()才能訪問,然后第二維的指針指向列,只要當(dāng)你去rs.getXXX(列)時,才通過
    Connection再去數(shù)據(jù)庫把真實的數(shù)據(jù)取出來,否則沒有什么機器能真的把要取的數(shù)據(jù)都放在內(nèi)
    存中.
        所以,千萬要記住,如果Connection已經(jīng)關(guān)閉,那是不可能再從ResultSet中取到數(shù)據(jù)的.
    有很多人問我,我可不可以取到一個ResultSet把它寫到Session中然后關(guān)閉Connection,這樣就
    不要每次都連結(jié)了.我只能告訴你,你的想法非常好,但,是錯誤的!當(dāng)然在javax.sql包中JDBC高
    級應(yīng)用中有CacheRow和WebCacheRow可以把結(jié)果集緩存下來,但那和我們自己開一個數(shù)據(jù)結(jié)構(gòu)把
    ResultSet的行集中所有值一次取出來保存起來沒有什么兩樣.
        訪問行中的列,可以按字段名或索引來訪問.下面是一個簡單的檢索結(jié)果的程序:

        ResultSet rs = stmt.executeQuery("select a1,a2,a3 from table");
        while(rs.next()){
            int i = rs.getInt(1);
            String a = rs.getString("a2");
            ..............
        }

        對于用來顯示的結(jié)果集,用while來進行next()是最普通的,如果next()返回false,則
    說明已經(jīng)沒有可用的行了.但有時我們可能連一行都沒有,而如果有記錄又不知道是多少行,這時
    如果要對有記錄和沒有記錄進行不同的處理,應(yīng)該用以下流程進行判斷:

        if(rs.next()){
            //因為已經(jīng)先next()了,所經(jīng)對記錄應(yīng)該用do{}while();來處理
            do{
                int i = rs.getInt(1);
                String a = rs.getString("a2");
            }while(rs.next());
        }
        esle{
            System.out.println("沒有取得符合條件的記錄!");
        }

        類型轉(zhuǎn)換:
        ResultSet的getXXX方法將努力把結(jié)果集中的SQL數(shù)據(jù)類型轉(zhuǎn)換為JAVA的數(shù)據(jù)類型,事實
    大多數(shù)類型是可以轉(zhuǎn)換的,但仍然有不少糊弄是不能轉(zhuǎn)換的,如你不能將一個SQL的float轉(zhuǎn)換成
    JAVA的DATE,你無法將 VARCHAR "我們"轉(zhuǎn)換成JAVA的Int.

        較大的值:
        對于大于Statement中g(shù)etMaxFieldSize返回值的值,用普通的getBytes()或getString()
    是不能讀取的,好在JAVA提供了讀取輸入浪的方法,對于大對象,我們可以通過rs.getXXXStream()
    來得到一個InputStream,XXX的類型包括Ascii,Binay,Unicode.根據(jù)你存儲的字段類型來使用不
    同的流類型,一般來說,二進制文件用getBinayStream(),文本文件用getAsciiStyream(),對于
    Unicode字符的文本文件用getUnicodeStream(),相對應(yīng)的數(shù)據(jù)庫字段類型應(yīng)該為:Blob,Clob和
    Nlob.

        獲取結(jié)果集的信息:
        大多數(shù)情況下編程人員對數(shù)據(jù)庫結(jié)構(gòu)是了解的,可以知道結(jié)果集中各列的情況,但有時并
    不知道結(jié)果集中有哪些列,是什么類型.這時可以通過getMetaData()來獲取結(jié)果集的情況:

        ResulSetMetaData rsmd = rs.getMetaData();
        rsmd.getColumnCount()返回列的個數(shù).
        getColumnLabel(int)返回該int所對應(yīng)的列的顯示標(biāo)題
        getColumnName(int)返回該int所對應(yīng)的列的在數(shù)據(jù)庫中的名稱.
        getColumnType(int)返回該int所對應(yīng)的列的在數(shù)據(jù)庫中的數(shù)據(jù)類型.
        getColumnTypeName(int)返回該int所對應(yīng)的列的數(shù)據(jù)類型在數(shù)據(jù)源中的名稱.
        isReadOnly(int)返回該int所對應(yīng)的列是否只讀.
        isNullable(int)返回該int所對應(yīng)的列是否可以為空
    posted on 2006-01-09 10:04 javaGrowing 閱讀(340) 評論(0)  編輯  收藏 所屬分類: JDBC

    只有注冊用戶登錄后才能發(fā)表評論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 国产在线观看xxxx免费| AA免费观看的1000部电影| 久久综合图区亚洲综合图区| 99在线观看精品免费99| 亚洲色欲啪啪久久WWW综合网| 亚洲精品第一国产综合境外资源 | h片在线免费观看| 久久亚洲中文无码咪咪爱| 亚洲综合网站色欲色欲| 黄页网站免费观看| 国产免费网站看v片在线| 国产成人亚洲合集青青草原精品| 久久亚洲2019中文字幕| 好先生在线观看免费播放| 成人毛片100免费观看| 亚洲第一区二区快射影院| 亚洲人成网7777777国产| 女人被免费视频网站| 今天免费中文字幕视频| 亚洲AV一区二区三区四区| 67pao强力打造67194在线午夜亚洲 | 黄色免费在线网站| 亚洲av无码无线在线观看| 亚洲综合在线观看视频| 亚洲第一黄色网址| 色妞WWW精品免费视频| 国产精品白浆在线观看免费| 黄页网站在线免费观看| 亚洲激情视频图片| 久久精品国产亚洲AV麻豆网站 | 亚洲热妇无码AV在线播放| 日韩精品视频免费网址| 国产国产人免费视频成69堂| a级片免费在线观看| 美女无遮挡免费视频网站| 2017亚洲男人天堂一| 亚洲国产综合精品中文第一区| 亚洲精品国产精品乱码不卞| 永久免费bbbbbb视频| 99在线视频免费观看视频 | 国产产在线精品亚洲AAVV|