隨筆 - 7, 文章 - 4, 評論 - 2, 引用 - 0
          數據加載中……

          杜克的面包店 -- 一個JDBC訂購系統原型,第2部分

          本文詳述了如何在Windows ME上使用Java 2 Platform, Standard Edition 1.3、Forte CE以及Microsoft Access。為了全面認識Forte CE的使用,請參閱Developing the Java 2D Art Applet Using Forte for Java Community Edition。也請參閱杜克的面包店 - 一個JDBC 訂購系統原型,第1部分,以獲取有關配置Microsoft Access的通用JDBC背景信息。

          杜克面包店 - 第1部分中,我創建了一個快速原型,向杜克面包店的擁有人Kate Cookie展示了如何可以使用Java和JDBC技術來創建一個訂購系統。自那時起,Kate就已經參加了一個Java編程班,并決定除了經營她的面包店外還要開始親自編寫Java代碼。因此我同意為她創建一個體系結構,作為她未來開發的起始點。我確定使用ResultSetMetaData來從數據庫中提取列名,以便在多數場合下,數據庫的變更會自動反映到代碼中。我也創建了Order Entry(訂單輸入)和Order Display(訂單顯示)窗口,因此如果添加或刪除了產品,這些改變就會自動在JTable顯示中反映出來。

          這個軟件體系結構將程序邏輯與Swing GUI生成代碼分離開來,因此程序邏輯的改變不會嚴重影響到GUI,GUI的改變也不會影響到程序邏輯。這種分離是"模型-視圖-控制器"(Model-View-Controller ,MVC)設計模式所推薦的。設計模式可能是創建可重用和可維護的軟件的關鍵因素。James W. Cooper在他的Java Design Patterns -A Tutorial中很好地闡述了這些技術。我已經選擇了要實現將程序邏輯和GUI呈現相分離的最基本想法。為此,我已經定義了一個小的Controller類來解決這些事情。下面列出了構造函數和main方法。
          public Controller() {
                  
            mod = new Model();
            dbm = new DBMaster( mod );
                  
          }//End Controller constructor
              
          public static void main (String args[]) {
                         
            new Controller() ;    
                  
          }//End main method
          

          Model類

          Model類包含了所有的程序邏輯并作為五個內部類實現,在各種JFrame擴展的GUI類中,這些內部類可作為JTable.setModel方法調用的參數,以創建JTable呈現用于數據顯示和數據輸入。Model也包含了許多數據庫處理方法,以便同JTable基礎結構配合使用。

          首先創建了數據庫Connection對象,然后實例化五個內部類。如下代碼所示:
          /* acquire the database connection object */
           dbc = getConnectionObj
               ( "jdbc:odbc:BakeryBook" , 
               "sun.jdbc.odbc.JdbcOdbcDriver" );
                  
           /* acquire the inner class objects */
           cqtm = new CustQueryTableModel ( dbc );
           cdtm = new CustDataTableModel  ( dbc );
           catm = new CustAddTableModel   ( dbc );
           cotm = new CustOrderTableModel ( dbc );
           chtm = new CustHistTableModel  ( dbc );
           cqtm.getDefaultResultsAddresses();
           cqtm.getDefaultResultsOrders();
          

          程序把數據庫Connection對象dbc傳遞給每個內部類的構造函數。Connection對象是通過我編寫的名為getConnectionObj的方法創建的。下面列出了它的代碼,該代碼體現了創建這個對象的標準操作過程,這在"杜克的面包店 - 第1部分"中已作過討論。
          public Connection 
            getConnectionObj( String url, String driver ) {
            try {
               Class.forName( driver );
               Connection db = 
                 DriverManager.getConnection( url );
               connectionSuccess = true;
               return db;
             }
             catch ( ClassNotFoundException cnfex ) {
               /* process ClassNotFoundExceptions here */
               cnfex.printStackTrace();
               return null;
             }
             catch ( SQLException sqlex ) {
               /* process SQLExceptions here */
               sqlex.printStackTrace();
               return null;
             }
             catch ( Exception excp ) {
              /* process remaining Exceptions here */
               excp.printStackTrace();
               return null;
             }//End try-catch          
           
           }//End getConnectionObj method    
          
          

          布爾型的connectionSuccess被初始化為false,只有在控制流轉到其中的一個catch塊,才會將其值設為true。

          下面列出了兩個方法調用,它們對于本應用程序的功能是非常重要的。

          cqtm.getDefaultResultsAddresses();
          cqtm.getDefaultResultsOrders();
          

          這些調用為Addresses和Orders表創建了ResultSetMetaData對象。這些元數據對象會在整個應用程序中用到。如下是getDefaultResultsAddresses的代碼,它與getDefaultResultsOrders方法相類似。
          public void getDefaultResultsAddresses() {
            try {
              statementAddresses = dbc.createStatement();
              rsAddresses = statementAddresses.executeQuery
                ("SELECT * FROM Addresses");
              rsAddressesMetaData = 
                rsAddresses.getMetaData();
             }//End try
             catch ( SQLException sqlex ) {
                jTextArea.append( sqlex.toString() ); 
             }//catch
             catch ( Exception excp ) {
                // process remaining Exceptions here
                 jTextArea.append( excp.toString() ); 
           }//End try-catch  
          }//End getDefaultResultsAddresses method
          

          這個方法創建了ResultSetMetaData對象rsAddressesMetaData。第一步是通過一個代表所有列的SQL *來從Addresses表中檢索數據,從而創建ResultSet對象rsAddresses。然后使用rsAddresses.getMetaData方法調用提取ResultSetMetaData 對象。rsAddressesMetaData 對象可在后面用于從Addresss表中提取列名和其他有用的信息。也創建了另一個類似的對象rsOrdersMetaData 。這兩對象可在整個應用程序中使用,這是通過使用cqtm.getAddressesMetaData方法和 cqtm.getOrdersMetaData方法調用來完成的,這兩個方法調用會返回一個ResultSetMetaData對象。如果在getDefaultResultsAddresses的try-cath邏輯塊中捕獲了一個錯誤,就會在 DBMaster窗口上的JTextArea對象中寫入錯誤信息。

          DBMaster類

          然后將 Model對象傳遞給DBMaster類(它是一個主Swing窗口),程序就跳轉到其他點以執行所有的其他功能。使用這種體系結構,每個Swing JFrame擴展類就可以通過將單一的 Model對象作為構造函數參數傳遞,來訪問所有的程序邏輯。當單擊按鈕產生其他的各種功能時,就會將Model對象傳遞到其他的JFrame擴展類。一旦GUI類構造函數得到該參數,就會提取這些內部類以供使用。讓我們來看一下從構造函數開始的一些DBMaster代碼。
          public DBMaster( Model model ) {
                  
            mod    = model;
            cqtm   = mod.getCustQueryTableModel();
            cdtm   = mod.getCustDataTableModel();
            cotm   = mod.getCustOrderTableModel();
            chtm   = mod.getCustHistTableModel();
            catm   = mod.getCustAddTableModel();
            rsMeta = cqtm.getAddressesMetaData();
          

          首先為全部類的作用域創建一個的 Model對象 mod。然后通過標準訪問器(accessor)方法使用mod對象來提取每個內部類對象。下面列出其中的一個存訪問器。

          public CustQueryTableModel getCustQueryTableModel() {
            return cqtm;
          }
          

          這個訪問器是作為一個標準的過程來實現的(即使不使用對象),以支持可能的功能修改。也從Address表中檢索了 ResultSetMetaData對象rsMeta,以提供有關后面要用到的Address表的信息。

          下面列出了DBMaster中內部類對象的定義。
          private Model.CustQueryTableModel cqtm;
          private Model.CustDataTableModel  cdtm;
          private Model.CustOrderTableModel cotm;
          private Model.CustHistTableModel  chtm;
          private Model.CustAddTableModel   catm;
          

          接下來通過下面的代碼呈現了GUI。
          SwingUtilities.invokeLater( new Runnable() {
            public void run() { 
                          
            initComponents ();
           
            setSize ( 750, 600 );   
            setVisible( true );
            mod.setJTextArea(jTextArea1);
            if (mod.getConnectionSuccess()) 
               jTextArea1.append
               ("Database Connection Successful\n");
            else
               jTextArea1.append
               ("Database Connection Failed\n");
                      
            }//End run 
          });//End invokeLater anonymous inner class
          

          SwingUtilities.invokeLater方法發出一個請求,以執行事件隊列中的一個代碼塊,然后從該代碼塊中返回并繼續執行。在本例中,創建了一個擴展了 Runnable的匿名內部類,因此該代碼可在它的run方法的內部執行。這保證了那些代碼是在事件調度線程上執行的,也保證了GUI呈現操作是"線程安全"的。

          調用initComponents方法會執行所有的設置代碼,這些代碼是由Forte CE使用GridBagLayout來生成的。使用Forte CE的一般可行辦法是使用AbsoluteLayout來做GUI設計,然后將其轉換成GridBagLayout以生成可移植的代碼。這種辦法工作得不錯,但有時需要調整GridBagLayout的很多復雜屬性。除非您深入理解GridBagLayout,否則下面的這種辦法是比較容易的:在GridBagLayout和AbsoluteLayout間來回轉換并且操縱Swing組件,直到您取得需要的結果。通常,這個過程可以很快地完成。

          getConnectionSuccess方法返回一個布爾型的數值,指出數據庫連接是否已經創建。如果是,就在jTextArea1對象中寫入一條消息,該對象是本應用程序的消息中心。其他的各種窗口有一些小的消息區域,但DBMaster中的JTextArea對象jTextArea1是主要的信息庫。

          DBMaster窗口

          DBMaster窗口看起來像下面這樣。

          點擊放大

          該窗口提供了兩個選項:Customer Info(客戶信息)和New Customer(新建客戶)。New Customer功能等同于作為另一窗口的一部分存在的一個功能,因此我們現在重點放在Customer Info上。如下代碼將幫我們實現Customer Info功能。Customer Info是由CustQuery對象custQuery來處理的。
          if ( custQuery != null ) {
             /* if CustQuery window is open with data */
             /* displayed, then reestablish it */
             /* with no data, and kill hide CustData */
             /* window, if open */
             custQuery.closeCustDataWindow();
             custQuery.setVisible(false);
             custQuery = new CustQuery( mod );
             cqtm.setQueryString( null );
             cqtm.setColString( getColumnName( 4 ) );
             cqtm.setQueryAll( false );
             cqtm.tableQuery();
             cqtm.fire();
           }//End if 
           else { 
              custQuery = new CustQuery( mod );
           }//End if-else      
          

          如果custQuery不為null,那么該custQuery窗口就已經處于活動狀態。第一個代碼塊展示了對custQuery.closeCustDataWindow方法的調用,該調用關閉了以前打開的各種被調用窗口(如果有的話),這些窗口可能干擾用戶的視界。然后讓現有的 CustQuery窗口變為不可見,并使用Model對象mod作為構造函數參數來重新實例化custQuery對象。接下來針對CustQueryTableModel對象cqtm執行了一些訪問器方法,該對象輸入查詢變量以執行cqtm.tableQuery方法。在本例中,其意圖是生成一個空的ResultSet對象,以便在呈現CustQuery窗口后,JTable顯示將以空內容的形式出現。下一節我們將看到這種表生成邏輯的一些細節問題。執行cqtm.tableQuery方法后,就會調用cqtm.fire方法激活表中數據。

          CustQuery窗口

          如下是帶有JTable列表的CustQuery窗口,其中列表是通過點擊Query All按扭得以初始化的。

          點擊放大

          如果通過單選按扭或單擊Query All按扭來初始化一個查詢,就會產生一個包含0條至完整的記錄列表,且包含來自Address表的所有行的列數據的一個子集的JTable。如果表中數據不為空,那么單擊其中的一行將自動產生另一個窗口(CustData),顯示該客戶記錄的列數值的完整集合,并且提供了其他的各種處理選項。

          如下是CustQuery tableQuery方法,它控制著這張表中的數據的生成。
          public void tableQuery() {
            try {
               if ( queryAll) {
                   /* order by last name, first name */
                   query = 
                   "SELECT * FROM Addresses ORDER BY " +
                      rsAddressesMetaData.getColumnName(
                      3) + "," +
                      rsAddressesMetaData.getColumnName(2);
                }//End if
                else {
                   /* order by last name, first name */
                   query = "SELECT * FROM Addresses WHERE " +
                   colstring +
                   " = " + "'" + qstring +
                    "'" + " ORDER BY " +
                   rsAddressesMetaData.getColumnName(
                   3) + "," +
                   rsAddressesMetaData.getColumnName(
                   2);
                }//End if-else
             
                /* argument list below allows for use of */
                /* ResultSet absolute method */
                statement =
                   dbc.createStatement
                   (ResultSet.TYPE_SCROLL_SENSITIVE,
                    ResultSet.CONCUR_READ_ONLY);
                    
                rs = statement.executeQuery( query );
                rsMeta = rs.getMetaData();
                /* extract column names using
                ResultSetMetaData */
                /* last name, first name, primary phone */
                colheads[0] = rsMeta.getColumnName(2);
                colheads[1] = rsMeta.getColumnName(3);
                colheads[2] = rsMeta.getColumnName(4);
                colheads[3] = rsMeta.getColumnName(10);
                                   
                jTextArea.append( "Sending query: " +
                 query + "\n" );
                       totalrows = new Vector();
                while ( rs.next() ) {
                  String[] record =
                  new String[ rsMeta.getColumnCount() - 1 ];
                   
                     record[0] = rs.getString( colheads[0] );
                     record[1] = rs.getString( colheads[1] );
                     record[2] = rs.getString( colheads[2] );
                     record[3] = rs.getString( colheads[3] );
          
                     totalrows.addElement( record );
                }//End while loop
                jTextArea.append( "Query successful\n" );
          
            }//End try
            catch ( SQLException sqlex ) {
              jTextArea.append( sqlex.toString() );
            }
            catch ( Exception excp ) {
              jTextArea.append( excp.toString() );
            }//End try-catch
          
          }//End tableQuery method
          

          如果布爾型的queryAll設為true,那么將把SQL字符串變量指派來從Addresses表中提取所有列和所有行,并將姓(last name)作為第一排序,名(first name)作為第二排序。else塊用于處理單擊上圖中三個單選按扭中的一個按鈕時所產生的查詢。文本輸入字段對應于 Last_Name、Primary_Phone和Company_Name。

          單擊單選按鈕時所獲取的文本字符串(qstring和colstring)是通過使用set方法在內部類(cqtm)中注冊的。qstring變量是從三個JText字段之一中提取的。colstring變量包含了使用ResultSetMetaData getColumnName方法調用提取的列名。SQL字符串與queryAll例子的不同只在于它添加了WHERE語法,用以限制搜索滿足一定條件的行(通過qstring和colstring選擇值來指定)。

          接下來使用下面的語法創建了Statement對象。

          statement = 
             dbc.createStatement
             (ResultSet.TYPE_SCROLL_SENSITIVE,
              ResultSet.CONCUR_READ_ONLY);
          

          傳遞給createStatement方法調用的參數啟用了ResultSet的滾動特性。在本應用程序中,我使用的是ResultSet方法absolute,它使得可以通過指定行號來訪問特定的行,而不必在數據中連續地滾動以查找一個指定的行。這種用法會在后面描述到。

          然后根據SQL字符串查詢,使用Statement對象來生成一個ResultSet對象,該rs對象用于創建一個ResultSetMetaData對象rsMeta。

          rs = statement.executeQuery( query ); 
          rsMeta = rs.getMetaData();
          

          使用ResultSetMetaData對象rsMeta,下面的代碼為JTable的注釋提取了列名。字符串數組變量包含First_Name、Last_Name、Primary_Phone和Company_Name。再請再注意一下,如果Addresses中的列名改變了,本應用程序將接受這些改變而不必修改軟件,因為數據并沒有被硬編碼。

          colheads[0] = rsMeta.getColumnName(2);
          colheads[1] = rsMeta.getColumnName(3);
          colheads[2] = rsMeta.getColumnName(4);
          colheads[3] = rsMeta.getColumnName(10);
          

          下面的while循環為JTable生成加載了數據結構。
          totalrows = new Vector();
          while ( rs.next() ) {
            String[] record = 
            new String[ rsMeta.getColumnCount() - 1 ];
                        
            record[0] = rs.getString( colheads[0] );
            record[1] = rs.getString( colheads[1] );
            record[2] = rs.getString( colheads[2] );
            record[3] = rs.getString( colheads[3] );
                         
            totalrows.addElement( record );
           }//End while loop
           jTextArea.append( "Query successful\n" ); 
          

          while循環假定了指針初始位于 ResultSet第一條記錄開始處的前面。如果有記錄可供讀取,那么每次調用next方法將返回一個布爾值true。如果該調用返回false,那么ResultSet的指針就已經移到了盡頭。一旦進入while循環,就會使用ResultSetMetaData來實例化一個字符串數組record,以指出檢索到的列數(如值rsMeta.getColumnCount() - 1所指出的)。因為Java數組是基于0的,因此其值必須加1。然后通過調用getString方法,就可以在該字符串數組(record)中加載ResultSet(rs)中的數值。 包含在colheads字符串數組中的列名用于按列名從rs中提取想要的數據。然后調用addElement方法將這條記錄添加到Vector對象totalrows中。因此我們正在創建的是使用字符串數組作為元素的Vector。當執行fire方法調用時,JTable表示基礎結構將自動呈現表中數據。

          我已經提到,在整個應用程序中都會用到ResultSetMetaData。CustQuery類使用這些數據來注釋GUI上的某些數據字段。如果使用Forte CE的Component Inspector,就是從方法的執行中生成字段名。下面描述了實現這種設置的工具。Properties選項卡下的text按鈕使用戶可在其上輸入代碼,以生成屏幕上的文本。在這里,我有意輸入的方法是getColumnName。

          點擊放大

          getColumnName方法包含CustQuery 類中,下面列出了這個方法。它基本上只是為rsMeta.getColumnName (i)的執行提供了一個方便的環境。
          public String getColumnName( int i ) {
            try {
               return rsMeta.getColumnName( i );
            }//End try
            catch ( SQLException sqlex ) {
               jTextArea.append( sqlex.toString() );
               return null;
            }//catch
            catch ( Exception excp ) {
              // process remaining Exceptions here
              jTextArea.append( excp.toString() );
              return null;
            }//End try-catch
          }//End getColumnName method
          

          在使用帶有JTables的Forte CE中,數據模型對象通常是使用Component Inspector工具來創建的。

          點擊放大

          在JTable的Component Inspector選項卡中,單擊model。在字段右邊出現了三個點。單擊三個點將產生上面圖像中描繪的窗口。在Form Connection選項卡中,單擊標示為User Code的單選按扭,然后輸入表示列模型的對象的名稱。在本例中,它是Model的一個內部類--CustQueryTableModel對象cqtm。

          在CustQuery類中,我們也使用Component Inspector來為JTable創建一個鼠標單擊事件處理方法。單擊Events選項卡中的mouseClicked按鈕,將產生一個已生成的方法名。下面描繪了Component Inspector窗口。

          點擊放大

          單擊return將導致Forte CE在CustQuery類中為JTable鼠標單擊處理生成一個空的方法。如下是帶有的表單擊處理代碼的該方法。
          private void 
              jTable2MouseClicked(
              java.awt.event.MouseEvent evt) {
           
              tablerow = jTable2.getSelectedRow();
              cdtm.setTableRow(tablerow);
                  
              if (custData != null) {
                  custData.setVisible(false);
                  custData = new CustData( mod );
               }//End if
               else { 
                  custData = new CustData( mod );
               }//End if-else
               
           }  
          

          我使用JTable方法getSelectedRow來提取鼠標單擊事件所選擇的那個表行。也使用了一組方法來將這些數據傳送給CustDataTableModel內部類以備后用。

          CustData窗口

          如前面的一些例子那樣,如果一個CustData窗口是處于活動的,那么就先使其不可見,然后再重新實例化它。如果沒有活動的CustData窗口,那就實例化一個新的CustData窗口。

          下面描繪了CustData窗口。

          點擊放大

          現在讓我們來看一下CustData類內部發生了什么。

          如前面所討論的,對于我的所有JFrame擴展GUI類,大多數CustData構造函數代碼都是標準的。那里有的三個重要的獨特語句。

          cdtm.setJTextField( jTextField1 );
          cdtm.tableQuery(); cdtm.fire();
          

          set語句將 JTextField對象jTextField1傳送給CustDataTableModel內部類,因此在處理期間,查詢邏輯可向CustData的GUI上的反饋字段寫入信息。

          cdtm.tableQuery和cdtm.fire方法調用創建了CustData JTable。下面列出了tableQuery的代碼。
          public void tableQuery() {
                 
            try {
               rs = cqtm.getResultSet();
               rsMeta = cqtm.getAddressesMetaData();
               totalrows = new Vector();
               /* point to the row corresponding */
               /* to the JTable click */
               rs.absolute(tblrow+1);
               /* get the autonumber index */
               index = rs.getString(1);
               colstring = rsMeta.getColumnName(1);
               /* create customer data table */
               for(int i=2;i<=
               rsMeta.getColumnCount();i++){
                  String[] rec = new String[30];
                  rec[0] = rsMeta.getColumnName(i);
                  rec[1] = rs.getString(i);
                  totalrows.addElement( rec );
               }//End for loop
                 jTextArea.append( "Query successful\n" );
               }//End try
               catch ( SQLException sqlex ) {
                  /* write to DBMaster msg area */
                  jTextArea.append( sqlex.toString() );
               }//catch
               catch ( Exception excp ) {
                 // process remaining Exceptions here
                 /* write to DBMaster msg area */
                 jTextArea.append( excp.toString() );
            }//End try-catch
          }//End tableQuery method
          

          rs = cqtm.getResultSet;語句用于提取單擊CustQuery JTable中的某個客戶時所生成的對象ResultSet 。這個ResultSet將總是只包含一行的數據。但我將這行的一些列名用作CustData JTable顯示中第一列的一些元素。數據元素用作第二列的元素。這聽起來有點復雜,但隨著我們進一步分析代碼,這一切將會變得明朗起來。

          第二條語句是rsMeta = cqtm.getAddressesMetaData;,用于從它前面創建的庫中提取ResultSetMetaData對象。在本例中,該對象也可從rs.getMetaData的執行中生成。在本應用程序中,我們有時并不是那么容易訪問 ResultSet對象來執行這個getMetaData方法。這就是選擇getAddressesMetaData方法的理由。

          接下來實例化了一個Vector對象totalrows。然后執行ResultSet 方法rs.absolute(tblrow+1);(JDBC 2.0中新增的功能) 。這將結果集指針移到對應于 JTable單擊行的記錄。注意,JTable 單擊行是基于0的索引,而ResultSet方法是基于1的,因此其值必須增加1。

          自動增量索引段(在一個插入操作期間由數據庫軟件生成)是通過語句index = rs.getString(1); 來提取的。該字段的列名是通過下面的方法調用來取得的:colstring = rsMeta.getColumnName(1);。

          然后使用下面的代碼塊來生成這張表。
          for(int i=2;i<=rsMeta.getColumnCount();i++){
             String[] rec = new String[30];
             rec[0] = rsMeta.getColumnName(i);
             rec[1] = rs.getString(i);
             totalrows.addElement( rec );
          }//End for loop
          

          上面的代碼創建了一個名為rec的字符串數組,其包含有數據對--列名及其值。然后將這對數據連續地添加到Vector對象totalrows中。一旦生成了這個數據結構,它就可用于自動的JTable生成(主要通過getValueAt 方法完成)。下面的列出了getValueAt方法。

          public Object getValueAt(int row, int col) {
           return ((String[])totalrows.elementAt(
           row)) [col];
          }
          

          這個方法首先從 Vector對象totalrows中提取完整的一行(一對字符串值),然后根據整型 [col]從該數組中提取一個值,從而取得實際的字符串元素。

          CustData窗口是應用程序程序處理的主要窗口。它提供了下面的一些操作。

          • 更新
          • 刪除
          • 新建客戶
          • 下訂單
          • 訂單歷史

          CustData窗口中的更新

          為了從窗口中進行更新,請單擊表中字段使其可以編輯(右邊的列)。如果有字符串的話,光標就定位在現有字符串的后面。然后現有文本可以被替換或添加,另外,只要按下的回車鍵或用鼠標點擊其他字段,編輯過的值可以通過getValueAt方法訪問。如果沒有滿足這個要求,即使在表字段中有了可見的文本,但這些數據也不會輸入,更新將不能正常進行。但應該說明的是,JTable是非常靈活的,可以將其配置來對各種各樣的鍵盤驅動和鼠標驅動的事件作出反應。

          如下代碼塊是在單擊Update按扭后觸發的。

          /* update button clicked */
                                      
          cdtm.tableUpdate();
          

          然后執行CustDataTableModel cdtm.tableUpdate方法。下面列出了更新代碼。
          public void tableUpdate() {
          
            try {
               int i;
               strgBuffer = 
                  getUpdateStatement
                  ( rsMeta.getColumnCount(
                  ), strgBuffer );
               String strgArg = strgBuffer.toString();
               pstatUpdate = dbc.prepareStatement(
                strgArg );
               for ( i=0; i < cdtm.getRowCount(
               ); i++ ) {
               String tableString;
                 if ( i == 2 ) {
                     tableString = stripOut
                     ( (String)cdtm.getValueAt( i, 1 ) );
                 } else {
                   tableString = (String)cdtm.getValueAt(
                    i, 1 );
                 }//End if-else
                 pstatUpdate.setString(
                  i+1, tableString ); 
               }//End for loop
               pstatUpdate.setString( i+1, index );
              
               pstatUpdate.executeUpdate();
                    
               jTextField.setText("Update successful");
               jTextArea.append("Update successful\n");    
               cqtm.tableQuery();
               cqtm.fire();
           
            }//End try
            catch ( SQLException sqlex ) {
              jTextField.setText(
              "SQL Error-see DBMaster");
              jTextArea.append( sqlex.toString() );
              return;
            }
            catch ( Exception excp ) {
                // process remaining Exceptions here
                jTextField.setText(
                "Error-see DBMaster");
                jTextArea.append( excp.toString() );
                return;
            }//End try-catch 
           
          }//End method tableUpdate   
          

          tableUpdate方法使用了PreparedStatement對象。這種技術的主要優點是可提高執行速度。在多數場合,如果使用了這種技術,將會馬上把SQL語句發送到DBMS,然后在那里進行編譯。其結果是,PreparedStatement對象包含了一條經過預編譯的SQL語句。 然后通過setXXX方法將值提供給PreparedStatement對象。在本更新操作的情形下,將一個具有如下形式的字符串作為參數傳遞給Connection prepareStatement方法dbc.prepareStatement( strgArg ):

          UPDATE Addresses SET  First_Name = ?,
           Last_Name = ?, ?  WHERE  AddrID = ?
          

          這個字符串是通過調用getUpdateStatement方法來生成的,該方法使用ResultSetMetaData訪問列名來生成SQL字符串。如下是getUpdateStatement方法的代碼。
          public StringBuffer 
            getUpdateStatement(
            int j, StringBuffer preparedSQL ){
            preparedSQL = new StringBuffer( 700 );
            preparedSQL.append("UPDATE Addresses SET  ");
            try {
               int i;
               for ( i=2; i < j ; i++ ) { 
                  preparedSQL.append(
                  rsMeta.getColumnName(i)+" = ?, ");
               }//End for loop
               preparedSQL.append(
               rsMeta.getColumnName(i)+" = ? WHERE " + 
               rsMeta.getColumnName(1) + " = ? " );
               }//End try
               catch ( SQLException sqlex ) {
                  /* write to DBMaster msg area */
                  jTextArea.append( sqlex.toString() );
                  sqlex.printStackTrace();
                  }//catch
                  catch ( Exception excp ) {
                    // process remaining Exceptions here
                    /* write to DBMaster msg area */
                    jTextArea.append( excp.toString() );
                    excp.printStackTrace();
                  }//End try-catch
                 
                  return preparedSQL;
             }//End getInsertStatement method
          

          該方法首先實例化一個StringBuffer對象,然后使用append方法調用來不斷地添加PreparedStatement SQL命令字符串。這里使用了Address表的所有列。WHERE搜索規則使用來定位由Addresses中第1列的自動編號字段AddrID更新的記錄。像上面一樣,在本應用程序中,列名是通過使用ResultSetMetaData對象來提取的。上面列出的getUpdateStatement 方法將該命令字符串返回到StringBuffer中,然后必須把該StringBuffer轉換成一個字符串對象,并將其傳遞給下面的語句。

          String strgArg = strgBuffer.toString();
          pstat = dbc.prepareStatement( strgArg );
          

          Connection方法prepareStatement創建了PreparedStatement對象pstat,然后該對象通過下面的for循環為執行做好了準備,該循環為SQL字符串中的問號占位符?提供了數值。
          for ( i=0; i < cdtm.getRowCount(
          ); i++ ) {
          String tableString;
             if ( i == 2 ) {
                tableString = stripOut
                ( (String)cdtm.getValueAt(
                 i, 1 ) );
             } else {
                tableString = (String)cdtm.getValueAt(
                 i, 1 );
             }//End if-else
             pstatUpdate.setString(
              i+1, tableString ); 
          }//End for loop
          pstatUpdate.setString(
           i+1, index );
          

          index字符串是在執行CustQueryTableModel queryTable方法期間創建的。它是Address表的第一列中的惟一自動編號字段。index字符串用來指出要更新的記錄。它的值是通過上面代碼塊中最后一條語句來輸入的:pstatUpdate.setString( i+1, index );。

          注意上面代碼中的stripOut方法調用。這是用來移除所有非數字字符的方法。我只將它應用于Primary_Phone列的數值。
          public String stripOut( String strg ) {
          /* strip out non-numeric characters */
          String numStrg = new String();
             for ( int i =
              0; i < strg.length(); i++ ) {
               if ( strg.charAt(i) >=
                  '0' && strg.charAt(i) <= '9' ) {
                   numStrg += strg.substring(i,i+1);
                }//End if
             }//End for loop
             return numStrg;
          }//End stripOut method
          

          這些代碼返回一個字符串值。請記住,當使用String方法substring時,第二個索引必須增加1以便提取一個字符,然后將該字符反復地添加到輸出字符串變量中。

          注意到下面這點也是重要的:cdtm.getValueAt方法調用是從JTable字段中提取數值,而不是從數據庫中提取數值。接受了JTable的所有字段后并執行下面的PreparedStatement方法,就會將用戶所做的任何變更輸入到數據庫中。 pstat.executeUpdate();

          CustData窗口中的刪除

          當用戶單擊CustData GUI上的delete按鈕時,就會執行下面的代碼。

          cdtm.custDelete();
          setVisible( false );
          

          在執行custDelete方法后,CustData窗口就變得不可見,因此用戶可以返回到CustQuery GUI,然后選擇其他操作。如下是在CustDataTableModel內部類中觸發的一個方法。
          public void custDelete() { 
            
            try { 
               batchError = false; 
               statement = dbc.createStatement();
               statement.addBatch(
               "DELETE FROM Addresses WHERE " + 
                  colstring + " = " + index );
               statement.addBatch(
               "DELETE FROM Orders WHERE " + 
                  colstring + " = " + index ); 
               batchErrorCodes = 
               statement.executeBatch();
               cqtm.setQueryAll( false );
               cqtm.setQueryString( null );
               cqtm.setColString(
                rsMeta.getColumnName( 4 ) );
             }//End try
             catch ( SQLException sqlex ) {
               jTextArea.append( sqlex.toString() );
               jTextField.setText(
                "SQL Error-See DBMaster" );
               return;
             }//End catch
             catch ( Exception excp ) {
               // process remaining Exceptions here
               jTextArea.append( excp.toString(
               ) );
               jTextField.setText(
                "Error-See DBMaster" );
               return;
             }//End try-catch block
                  
             batchError = false;
             for (int i=0; i < 
             batchErrorCodes.length; i++ ) {
                      
                if ( batchErrorCodes[i] > 0 ) {
                   jTextArea.append(
                    "Delete Successful\n" );
                 }//End if 
                 else if (
                  i == 1 && batchErrorCodes[i] == 0 ) {
                    jTextArea.append(
                    "No Orders Records to Delete\n" );
                 } 
                 else {
                    jTextArea.append(
                     "Delete Error\n" );
                    jTextField.setText(
                     "Delete Error" );
                    batchError = true;
                 }//End if-else
                      
                 if ( !batchError ) 
                    jTextField.setText( 
                    "Delete Successful" );
                        
              
                 tableClear();
                 cqtm.tableQuery();
                 cqtm.fire();
                  
             }//End for loop
          
          }//End custDelete method
          

          批量更新(Batch Update)是JDBC核心API引入的一個新特性。批量更新可能更加能有效,而且對于事務處理可能是特別有用的,那里一組事務中的一個操作失敗可能要求回滾整組事務。我還沒實現這一點,但我編寫的那些代碼可以容易地適用于這種方案(假定DBMS驅動器支持它)。您可以閱讀White, Fisher等人編寫的JDBC API Tutorial and Reference, Second Edition,以獲取有關該主題的進一步信息。

          讓我們來看一下批量更新是如何組裝起來的。

          batchError = false; 
          statement = dbc.createStatement();
          

          這些代碼行定義了用于錯誤處理的布爾型 batchError變量,然后按通常的方式--執行Connection對象dbc的createStatement方法--定義了一個Statement對象。然后執行下面的代碼行。
          statement.addBatch("DELETE FROM Addresses WHERE " + 
             colstring + " = " + index );
          statement.addBatch("DELETE FROM Orders WHERE " + 
             colstring + " = " + index ); 
          batchErrorCodes = statement.executeBatch();
          

          addBatch方法調用將SQL命令字符添加到批隊列中。然后executeBatch方法完成了刪除操作。在我的表schema中,AddrID字段是惟一的自動編號字段,它是在向Addresses表中插入一條新記錄時由DBMS軟件生成的。當配置一個訂單時,我在Orders表的第二列中重復了那個值。我給它取了相同的名字AddrID。因此,Addresses的第一列是AddrID,Orders表的第二列也是AddrID。輸入到Orders表中的多張訂單具有來自Addresses表的AddrID名稱。Orders表的第一列叫作CustID。本方案簡化了刪除操作的語法。相同的刪除語法(DELETE FROM <Table> WHERE AddrID = n)適用于兩張表,它會刪除與指定客戶有關的所有記錄。

          其余的代碼專門用于處理可能產生的各種錯誤條件。
          batchError = false;
          for (int i=0; i < batchErrorCodes.length; i++ ) {
          
             if ( batchErrorCodes[i] > 0 ) {
                 jTextArea.append( "Delete Successful\n" );
             }//End if
             else if ( i == 1 && batchErrorCodes[i] == 0 ) {
                jTextArea.append(
                 "No Orders Records to Delete\n" );
             }
             else {
                jTextArea.append( "Delete Error\n" );
                jTextField.setText( "Delete Error" );
                  batchError = true;
             }//End if-else
          
              if ( !batchError )
                jTextField.setText( "Delete Successful" );
          
             tableClear();
             cqtm.tableQuery();
             cqtm.fire();
          
                 }//End for loop
          

          executeBatch方法返回一個整型值的數組,其值對應于執行的操作數。在本例中,對于每個刪除操作返回一個值(即返回兩個值)。

          從一個成功刪除操作中期望返回的代碼是1。如是沒有數據可刪除,那就返回0,這里的一種情形是客戶沒有配置任何訂單。這些值被處理后,就會清除表中的字段,然后將消息反饋寫到本地的JtextField和DBMaster中的JTextArea,然后使用前面創建的null查詢參數來調用tableQuery方法,以便在CustQuery GUI上返回一個JTable。然后在CustData類代碼控制下關閉了CustData窗口,它當然取消發送數據到JTextField 區域,但出于完整性考慮,我包含了它。

          新建客戶

          單擊CustData GUI上的New Cust按鈕將執行下面的代碼。

          custAdd = new CustAdd( mod );
          catm.tablePopulate();
          catm.fire();
          

          這些代碼實例化了CustAdd類,產生了它的窗口,然后使用tablePopulate方法調用來呈現它的JTable。下面是該方法的代碼。
          public void tablePopulate() {
            try {
                 rsMeta = cqtm.getAddressesMetaData();
          	   totalrows = new Vector();
                 colstring = rsMeta.getColumnName( 1 );
                 for (int i = 2; i <=
                  rsMeta.getColumnCount(); i++ )  {           
                          String[] rec = new String[30];
                          rec[0] = rsMeta.getColumnName(i);
                          totalrows.addElement( rec );
                 }//End for loop
               }//End try
               catch ( SQLException sqlex ) {
                  jTextArea.append( sqlex.toString() );
               }//catch
               catch ( Exception excp ) {
                  // process remaining Exceptions here
                  jTextArea.append( excp.toString() );
             }//End try-catch
           }//End tableQuery method
          

          這個方法生成了一個JTable用于數據輸入,其結構與CustData JTable相同。for循環從2開始。因為第一列是自動編號字段AddrID,它是在插入操作期間由DBMS軟件自動生成的。另外,如我們前面看到,我們創建的是存儲了字符串數組對象的相同Vector對象,除了數據列沒有填充數值外。如您可以看到的,每次只將有一個字符串數組元素添加到Vector中。那個元素就是列名。for循環是由getColumnCount方法調用控制的。

          一旦CustAdd窗口處于活動狀態,通過輸入數據后單擊return或使用鼠標將焦點轉到一個新的字段上,就會在字段中注冊這些值。然后單擊Create Record按鈕。 Primary_Phone是添加操作中需要輸入數值的僅有的一個字段。下面展示了CustAdd GUI。

          點擊放大

          單擊Create Record按鈕會執行下面的代碼。

          catm.tableInsert();
          catm.fire();
          

          讓我們來看一下tableInsert方法。
          public void tableInsert() {
                 
             try {
                /* my method to build prepared */ 
                /* statement string */
                strgBuffer = getInsertStatement( 
                   rsMeta.getColumnCount(), strgBuffer );
                     
                /* convert StringBuffer to String */
                pstrg = strgBuffer.toString();
                pstat = dbc.prepareStatement( pstrg );
                /* Statement stmt = 
                dbc.createStatement(); */
                for ( int i=0; i < getRowCount(); i++ ) {
                   String strgVal = 
                   (String)getValueAt( i, 1 );
                   if( i == 2 ) {
                       if ( strgVal == null ) {
                          jTextField1.setText
                            ( "Primary_Phone required" );
                          jTextArea.append
                            ( "Primary phone 
                            field required\n" );
                          return;
                        }
                        pstat.setString(
                        i+1, stripOut( strgVal ) );
                    } else pstat.setString( i+1, strgVal ); 
                }//End for loop 
                pstat.executeUpdate();
                cqtm.tableQuery();
                cqtm.fire();
                jTextField1.setText("");
                jTextField1.setText(" Insert successful ");
                jTextArea.append
                   (" Insert into Addresses successful\n");
            }
              catch ( SQLException sqlex ) {
               jTextArea.append( sqlex.toString() );
               jTextField1.setText
                    ("SQL Error-see DBMaster window" );
            }
               catch ( Exception excp ) {
                // process remaining Exceptions here
                jTextArea.append( excp.toString() );
                jTextField1.setText(
                 "Error-see DBMaster window" );
            }//End try-catch                 
             }//End method tableInsert  
          

          getInsertStatement方法調用返回如下形式的一條SQL字符串:
          INSERT INTO Addresses (
          First_Name, Last_Name, ?) VALUES (?, ?, ?)
          
          
          strgBuffer = getInsertStatement( 
             rsMeta.getColumnCount(), strgBuffer );
                     
          /* convert StringBuffer to String */
          pstrg = strgBuffer.toString();
          pstat = dbc.prepareStatement( pstrg ); 
          

          然后將StringBuffer轉換成一個字符串,并將其作為一個參數進行傳遞,以便創建PreparedStatement對象pstat。然后下面的for循環使用getValueAt方法從表中字段提取輸入的數據。
          for ( int i=0; i < getRowCount(); i++ ) {
             String strgVal = (String)getValueAt( i, 1 );
             if( i == 2 ) {
                 if ( strgVal == null ) {
                    jTextField1.setText
                      ( "Primary_Phone required" );
                    jTextArea.append
                      ( "Primary phone field required\n" );
                    return;
                  }
                  pstat.setString(i+1, stripOut( strgVal ) );
              } else pstat.setString( i+1, strgVal ); 
          }//End for loop 
          

          然后使用pstat.setString方法調用來設置這些問號參數。注意,if( i == 2 )塊用于要求輸入Primary_Phone字段。如我們前面看到,stripOut方法可以移除非數字值。

          該操作最終由下面代碼完成。
          pstat.executeUpdate();
          cqtm.tableQuery();
          cqtm.fire();
          jTextField1.setText("");
          jTextField1.setText(" Insert successful ");
          jTextArea.append
          (" Insert into Addresses successful\n");
          

          PreparedStatement方法pstat.executeUpdate 在數據庫中輸入新的數據。然后刷新查詢表以反映新數據,并將消息寫到本地的JTextField和DBMaster上的JTextArea。

          下訂單

          通過單擊CustData窗口底部的Place Order單選按扭,可以實例化一個CustOrder窗口。單擊這個按扭執行了CustData中的如下代碼。
          if (custOrder != null) {
              custOrder.setVisible(false);
              custOrder = new CustOrder( mod );
           }
           else { 
              custOrder = new CustOrder( mod );
           }//End if-else
          

          我們使用現在大家都應該熟悉的技術來呈現CustOrder窗口。下面展示了這個GUI。

          點擊放大

          這些語句是由CustOrder構造函數執行的。

          cotm.tablePopulate();
          cotm.fire();
          

          在本例中,tablePopulate填寫了對應于Orders表中那些列名的一個兩列JTable。讓我們來看一下代碼。
          public void tablePopulate() {
                 
            try {
               rsMeta = cqtm.getOrdersMetaData();
          
          	totalrows = new Vector();
          
               colstring = rsMeta.getColumnName(1);
               for (int i = 4;i <= 
                     rsMeta.getColumnCount();i++){
                  String[] rec = 
                    new String[ rsMeta.getColumnCount(
                    ) - 3 ];
                  rec[0] = rsMeta.getColumnName( i );
                  totalrows.addElement( rec );  
               }//End for loop
              }//End try
              catch ( SQLException sqlex ) {
                 jTextArea.append( sqlex.toString() ); 
              }//catch
              catch ( Exception excp ) {
                  // process remaining Exceptions here
                  jTextArea.append( excp.toString() ); 
             }//End try-catch
           }//End tablePopulate method
          
          

          這個tablePopulate方法與我們前面分析的一個方法類似,但請注意,我們這次是要呈現一些用于輸出的 JTable字段,這些字段對應于Orders表,從第四列開始,一直進行到最后一列,并由rsMeta.getColumnCount方法調用控制。Orders表的第1-3列包含CustID、AddrID和Order_Date,它們不適合于用戶訂單輸入。

          當在CustOrder上單擊Place Order按鈕時,會執行下面的代碼。

          cotm.tableInsert();
          cotm.fire();
          

          下面是tableInsert的代碼清單。
          public void tableInsert() {
          
           /* get AutoNumber field from Addresses table */
           index = cdtm.getIndex();
          
           try {
             /* fire method to build prepared */
             /* statement string */
              preparedSQL = getInsertStatement(
                 rsMeta.getColumnCount(), preparedSQL );
             /* convert StringBuffer to String */
              String stringSQL = preparedSQL.toString();
              pstat = dbc.prepareStatement( stringSQL );
             /* get current date in SimpleDataFormat */
              java.util.Date date = new java.util.Date();
              SimpleDateFormat fmt =
                new SimpleDateFormat("yyyy.MM.dd-HH:mm z");
              String dateString = fmt.format( date );
             /* AutoNumber index field from Addresses table */
              pstat.setString( 1 , index );
              pstat.setString( 2, dateString );
              /* fill in product values */
              for ( int i=0; i < getRowCount(); i++ ) {
                pstat.setString(i+3,(String)getValueAt(i,1));
              }//End for loop
              pstat.executeUpdate();
              jTextArea.append(
               "Order Successfully Placed\n" );
              jTextField.setText( "Order Placed" );
             }//End try
             catch ( SQLException sqlex ) {
             jTextArea.append( sqlex.toString() );
             jTextField.setText( "SQL Error-See DBMaster" );
             }
             catch ( Exception excp ) {
               // process remaining Exceptions here
               jTextArea.append( excp.toString() );
               jTextField.setText( "Error-See DBMaster" );
           }//End try-catch
          }//End method tableUpdate
          

          這些代碼也類似于我們前面看到的代碼。由getInsertStatement方法調用生成的SQL字符串具有如下形式:

          INSERT INTO Orders (
           AddrID, Order_Date, ?) VALUES ( ?, ?, ?.)

          下面代碼用于處理日期字符串的生成。

          java.util.Date date = new java.util.Date();
          SimpleDateFormat fmt = 
            new SimpleDateFormat("yyyy.MM.dd-HH:mm z");
          String dateString = fmt.format( date );
          

          使用java.util.Date對象作為參數來調用SimpleDateFormat的format方法,會產生了類似于如下的一個日期字符串。

          2001.06.21-10:35 PDT

          SimpleDateFormat參數yyyy.MM.dd-HH:mm z指出日期字符串的形式。

          yyyy字符串對應于由"."分隔的一個四位年字段,MM指的是一個兩位的月字段,dd指出由"-"分隔的表示月份中某一天的兩位字段,然后HH指出一個兩位的0-23的小時字段,mm顯示分鐘字段,最后z指出時區。

          我以這種方式創建日期字段是為了在JTable數據表示期間方便排序。

          訂單歷史

          通過單擊CustData窗口底部的Order History單選按鈕,可以實例化一個CustOrderHist窗口。單擊這個按扭會執行CustData中的如下代碼。
          if (custOrderHist != null) {
                custOrderHist.setVisible(false);
                custOrderHist = new CustOrderHist( mod );
              }
              else { 
                 custOrderHist = new CustOrderHist( mod );
              }//End if-else
          

          對于本應用程序,CustOrderHist構造函數是標準的,但包含了一些特有的方面。
          chtm.tableQuery();
          chtm.fire(); 
          TableColumn tcol =
            jTable2.getColumnModel().getColumn(0);
          tcol.setPreferredWidth(125);
          

          首先,執行CustHistTableModel tableQuery方法和它的fire方法。

          然后使用setPreferredWidth方法重配置CustHistTableModel JTable,來擴展第一列的寬度以適應日期字符串。寬度設為125個像素。

          如下是CustOrderHist窗口。

          點擊放大

          上面的JTable又是CustDataTableModel對象cdtm的一個呈現,它最初在CustData類中使用。下面的JTable對于這個類來說是新的。它按日期排序,顯示該客戶的訂單,并將從Orders表中提取的產品作為列標題列出。再說明一下,如果產品種類增加了,這張表將自動接受這些改變,因為ResultSetMetaData是用來生成表和產品名稱的。

          對于這個GUI沒有什么可能的動作,在窗口創建后就會呈現這些表。如下是CustHistTableModel tableQuery方法。
          public void tableQuery() {
            
              rsMeta = cqtm.getOrdersMetaData();
              index = cdtm.getIndex();
          
              try {
                  String strg = "SELECT * FROM Orders " +
                      " WHERE " + rsMeta.getColumnName(2) +
                      " = "+ index + " ORDER BY " +
                  rsMeta.getColumnName(3);
                  rs = statement.executeQuery( strg );
          	  totalrows = new Vector();
          
                  while ( rs.next() ) {
                      String[] rec =
                         new String[rsMeta.getColumnCount()-2];
          
                      int j = 0;
                      for (int i=0;i<=
                      rsMeta.getColumnCount();i++) {
                          if ( i>2 ) {
                              rec[j]=rs.getString
                                  ( rsMeta.getColumnName(i));
                              j++;
                          }//End if block
          
                      }//End for loop
                   totalrows.addElement( rec );
          
                   }//End while loop
                   jTextArea.append(
                    "CustHist Query successful\n" );
                }//End try
                catch ( SQLException sqlex ) {
                   jTextArea.append( sqlex.toString() );
                }//catch
                catch ( Exception excp ) {
                   // process remaining Exceptions here
                   jTextArea.append( excp.toString() );
             }//End try-catch
          
          }//End tableQuery method
          

          第一步是從Addresses表中提取ResultSetMetaData對象和該客戶的自動編號字段值。

          rsMeta = cqtm.getOrdersMetaData();
          index = cdtm.getIndex();
          

          然后創建SQL語句。

          String strg = "SELECT * FROM Orders " +
           " WHERE " + rsMeta.getColumnName(2) +
           " = "+ index + " ORDER BY " + 
           rsMeta.getColumnName(3);
          

          rsMeta.getColumnName(2)訪問的第二列的字段名是AddrID,該字段對應于Addresses表中的同名自動編號字段。這條語句將從Addresses表中選出包含該客戶的自動編號字段的所有列的和所有記錄。如果客戶沒有訂購,那結果就可能為null。

          下面的代碼塊完成了這個方法。
          rs = statement.executeQuery( strg );
               totalrows = new Vector(); 
                      
          while ( rs.next() ) {
            String[] rec = 
               new String[rsMeta.getColumnCount()-2];    
                          
            for (int i=0;i<=
               rsMeta.getColumnCount();i++) {
               if ( i >2 ) {
                    rec[i-3]=rs.getString
                    ( rsMeta.getColumnName(i));
                }//End if block
             }//End for loop
          totalrows.addElement( rec );
                          
          }//End while loop
          jTextArea.append( "CustHist Query successful\n" ); 
          

          SQL字符串是由executeQuery方法執行的,然后使用while(rs.next())語法來反復訪問結果集,這我們已經在前面看到過。當結果集為空時,next方法返回false。

          其他的代碼是標準的過程,用于為JTable呈現創建Vector數據結構。惟一的竅門是操縱索引變量i,使其跳過Orders的前兩列,它們分別包含了自動編號字段和AddrID字段,后一字段將Orders中的記錄與Addresses中的一個客戶記錄關聯起來了。

          結束語

          本應用程序為杜克面包店的擁有者Kate Cookie提供了有關Java和JDBC技術的概覽,這對于她在添加和定制訂購系統以滿足她的要求時是有用的。它也提供了一個將GUI生成與處理代碼相分離的體系結構,這使得程序的維護和修改變得更加容易了。既然她已經看到運行中的本應用程序并學習了這個軟件,她就渴望做些編程工作。她告訴我她計劃為Orders表實現刪除/更改功能。此外,還可以感覺到她有一些其他想法。開始編碼吧。

          代碼清單

          enchilada.jar

          應用程序執行腳注

          為了在沒有使用Forte CE下運行本應用程序,您必須:

          1. 創建下面的目錄路徑(這里使用的是MS-DOS語法)。
            C:\Development\meloan
          2. 將 *.java 文件復制到meloan目錄。
          3. 轉到meloan目錄,在MS-DOS提示行中輸入:
            javac *.java
            以編譯所有的Java源文件。
          4. 將目錄改變到根(root)目錄,然后輸入:
            C:\>java Development.meloan.Controller

          記住,您必須安裝Microsoft Access,而且您必須將它設置來使用包含的BakeryBook.mdb數據庫文件。為獲取這個安裝過程的信息,請參閱杜克的面包店 - 第1部分的Microsoft Access部分。

          參考文章

          參考URL

          關于作者

          Michael Meloan,他經常為Java Developer Connection撰稿,他的職業生涯從編寫IBM大型機和DEC PDP-11匯編語言開始。他還在繼續使用PL/I, APL 和 C語言編寫代碼。另外,他的小說曾發表在WIRED, BUZZ, Chic, L.A. Weekly和National Public Radio上。

          posted on 2005-01-22 12:26 jacky 閱讀(556) 評論(0)  編輯  收藏


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


          網站導航:
           
          主站蜘蛛池模板: 福州市| 延边| 东兰县| 龙州县| 洪洞县| 甘南县| 洛阳市| 江阴市| 新巴尔虎左旗| 芦山县| 蕉岭县| 来安县| 遵义县| 万载县| 常州市| 台南市| 南雄市| 临猗县| 贡觉县| 巴彦淖尔市| 房山区| 吉首市| 平安县| 榆树市| 应城市| 西吉县| 洞头县| 莱芜市| 砚山县| 宣汉县| 内江市| 绥德县| 邯郸市| 彭水| 隆尧县| 左贡县| 阿坝县| 荥经县| 湘潭县| 海林市| 峨山|