javaGrowing

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

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

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

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

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


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

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

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

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

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

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

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

              按上面介紹的步驟,一個完成的例子是這樣的:(注意,為了按上面的步驟介紹,這個例
          子不是最好的)
              try{
                  Class.forName("org.gjt.mm.mysql.Driver");
              }catch(Exception e){
                  System.out.println("沒有成功加載驅動程序:"+e.toString());
                  return;
              }//對于象我這樣的經驗,可以直接從e.toString()的簡單的幾個字判斷出異常原因,
               //如果你是一個新手應該選捕獲它的子類,如何知道要捕獲哪幾個異常呢?一個簡單
               //的方法就是先不加try{},直接Class.forName("org.gjt.mm.mysql.Driver");,編
               //譯器就會告訴你要你捕獲哪幾個異常了,當然這是偷機取巧的方法,最好還是自己
               //去看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("數據庫操作出現異常:"+e.toString());
              }
              finally{
                  try{conn.close();}catch(Exception){}
              }//不管你以前是學習到的關于數據庫流程是如何操作的,如果你相信我,從現在開始,
               //請你一定要把數據庫關閉的代碼寫到finally塊中,切切!
          JDBC基礎(三)

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

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

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

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

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

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

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

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

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

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

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

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

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


          網站導航:
           
          主站蜘蛛池模板: 邓州市| 淮安市| 康马县| 和政县| 远安县| 潞西市| 桃园市| 寻甸| 正定县| 托克托县| 商河县| 广宗县| 龙胜| 宜章县| 卢湾区| 垣曲县| 夏河县| 福清市| 浑源县| 娄底市| 建宁县| 陆川县| 元氏县| 高邮市| 金阳县| 山阴县| 肇源县| 阆中市| 长顺县| 门头沟区| 杭州市| 舞阳县| 兴仁县| 玉门市| 炉霍县| 资阳市| 南雄市| 山东省| 锦州市| 盐山县| 怀集县|