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

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

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

    隨筆 - 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)  編輯  收藏


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


    網站導航:
     
    主站蜘蛛池模板: 国产免费变态视频网址网站| 日韩免费无砖专区2020狼| 亚洲V无码一区二区三区四区观看| 精品成人一区二区三区免费视频 | 亚洲av日韩综合一区久热| 无码日韩精品一区二区免费 | 在线亚洲v日韩v| 四虎1515hm免费国产| eeuss影院www天堂免费| 亚洲一区二区三区偷拍女厕| baoyu116.永久免费视频| 亚洲精品高清视频| 青青青免费国产在线视频小草| 亚洲国产成人精品无码区在线秒播| 四虎在线最新永久免费| 亚洲日韩国产欧美一区二区三区 | 日本高清色本免费现在观看| 无码一区二区三区亚洲人妻| 久久精品夜色噜噜亚洲A∨| 国精产品一区一区三区免费视频| 亚洲∧v久久久无码精品| 亚洲免费视频网址| 久久无码av亚洲精品色午夜| 亚洲综合区小说区激情区| 久久国产乱子伦精品免费不卡| 亚洲小视频在线播放| 国产又粗又长又硬免费视频| 两个人看的www免费高清| 亚洲最大的成网4438| 韩国欧洲一级毛片免费| 两个人日本WWW免费版| 亚洲一区电影在线观看| 亚洲成片观看四虎永久| 最近新韩国日本免费观看| 亚洲国产av玩弄放荡人妇| 国产AV无码专区亚洲AVJULIA| 一色屋成人免费精品网站 | 亚洲精品熟女国产| 无码欧精品亚洲日韩一区夜夜嗨 | 成年女人毛片免费播放人| 国产美女视频免费观看的网站|