< 上一個課程 | 下一個課程 >

執行查詢和顯示結果集

下面的程式碼範例是一個執行下列作業的 Swing 應用程式: 查詢的選取和執行是在執行方法中進行。 結果集的顯示是在 showResults 方法中進行。以下為完整的範例:
static void run(String title, CqProvider provider, Viewer viewer)
    throws WvcmException
    {
        ResourceList<CqUserDb> databases =
            setUserFriendlyLocation(Utilities
                .getUserDbList(provider,
                               new PropertyRequest(CqUserDb.USER_FRIENDLY_LOCATION)));
        CqUserDb userDb = (CqUserDb)JOptionPane.showInputDialog
            (null, "Choose a Database to Explore", title,
             JOptionPane.INFORMATION_MESSAGE, 
             null, databases.toArray(), databases.get(0));
    
        if (userDb == null) System.exit(0);
        
        userDb = (CqUserDb)userDb.doReadProperties
            (new PropertyRequest(CqUserDb.ALL_QUERIES.nest(CqQuery.USER_FRIENDLY_LOCATION)));
    
        // 將清單轉換成選項對話框中使用的排序陣列
        CqQuery[] queries =
            setUserFriendlyLocation(userDb.getAllQueries()).toArray(new CqQuery[]{});
    
        Arrays.sort(queries, new Comparator<CqQuery>(){
                public int compare(CqQuery arg0, CqQuery arg1)
                { return arg0.toString().compareTo(arg1.toString()); }});
    
        // 將查詢清單呈現給使用者並容許使用者選取其中之一
        CqQuery query = (CqQuery)JOptionPane.showInputDialog
                    (null, "Choose a Query to Execute",
                     "All Queries in " + userDb.location().string(), 
                     JOptionPane.INFORMATION_MESSAGE, null,
                     queries, queries[0]);
        
        if (query == null) System.exit(0);
        
        CqResultSet results =
            query.doExecute(1, Long.MAX_VALUE, CqQuery.COUNT_ROWS);
        
        // 如果適當地執行查詢,則將資料內部化並準備顯示它
        if (results.hasNext()) {
            // 從檢視器存取的直欄資訊
            g_columns = results.getColumnLabels();
            g_cell = new CqRowData[(int)results.getRowCount()];
    
            for (CqRowData row: results)
                (g_cell[(int)row.getRowNumber()-1] = row).getValues();
            
            // 顯示查詢結果資料
            showResults(query.location().string(), viewer);
        }
    }
    
    /** 結果集可供 GUI 元件存取並予以顯示 */
    static CqRowData[] g_cell;
    /** 直欄標題可供 GUI 元件存取並予以顯示 */
    static String[] g_columns;
    
    /**
     * 在表格中顯示結果集(在 g_cell 中)。
     * 
     * @param title 結果集視窗的標題字串
     * @param viewer 用於詳細顯示結果集的單一資源
     *            的 Viewer 實例。可能是空值,如果是這樣,
     *            不會呈現顯示單一資源的選項。
     */
    static void showResults(String title, final Viewer viewer) {
        // 定義 JTable 視窗的表格模型;每一個
        // 查詢顯示欄位有一個直欄,查詢結果集的每一列有一列。
        TableModel dataModel = new AbstractTableModel() {
    		 		 private static final long serialVersionUID = -3764643269044024406L;
    		 		 public int getColumnCount() { return g_columns.length; }
            public int getRowCount() { return g_cell.length;}
            public Object getValueAt(int row, int col) 
                { return g_cell[row].getValues()[col]; }
            public String getColumnName(int col)
                { return g_columns[col]; }
        };
        
        // 建構含有選用按鈕的查詢結果視窗,
        // 以顯示所選取列中的記錄(用於 View Record 及
        // Modify Record 範例)
        final JFrame frame = new JFrame(title);
        final JTable table = new JTable(dataModel);
        JPanel panel = new JPanel(new BorderLayout());
        
        if (viewer != null) {
            JButton button = new JButton("Open");
    
            panel.add(button, BorderLayout.SOUTH);
            button.addActionListener(new ActionListener(){
                    public void actionPerformed(ActionEvent arg0)
                    {
                        int[] selected = table.getSelectedRows();
                        
                        for (int i = 0; i < selected.length; ++i)
                            try {
                                viewer.view((CqRecord) g_cell[selected[i]]
                                    .getRecord());
                            } catch (WvcmException e) {
                                Utilities.exception(frame, "View Record", e);
                            }
                    }
                });
        }
        
        panel.add(new JScrollPane(table), BorderLayout.CENTER);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setContentPane(panel);
        frame.setBounds(300, 300, 600, 300);
        frame.setVisible(true);
    }
    
    static <U extends=""> ResourceList<U>
    setUserFriendlyLocation(ResourceList<U> list)
    throws WvcmException
    {
        for (U res: list)
            res.modifyLocation(res.getUserFriendlyLocation());
    
        return list;
    }
    
    /**
     * 物件的簡易介面,會顯示 Record 資源。
     *(用於 ExecuteQuery 範例的延伸規格。)
     */
    static interface Viewer {
        /**
         * 顯示 Record 資源
         * @param resource 要顯示之記錄的 Record proxy。
         * @return TODO
         */
        JFrame view(CqRecord resource);
    }
    
    /**
     * ExecuteQuery 範例的主程式。
     * @param args 不使用。
     * @throws Exception 如果提供者無法實例化。
     */
    public static void main(String[] args) throws Exception
    {
        try {
            run("Execute Query", Utilities.getProvider().cqProvider(), null);
        } catch(Throwable ex) {
            Utilities.exception(null, "Execute Query", ex);
            System.exit(0);
        }
    }

在這個範例中,如同前一課建構可用的資料庫清單。 該清單會呈現給使用者,供其選擇登入的單一資料庫。

使用者選取使用者資料庫之後,該資料庫的 ALL_QUERIES 內容會讀入應用程式中。此內容的值 為 CqQuery Proxy 的 ResourceList。此清單會依查詢的位置排序,並呈現給使用者,供其選擇要執行的單一查詢。

若要選擇資料庫和選擇查詢,則會使用同樣的通用 Swing 方法,即 JOptionPane.showInputDialog。 此輸入是供選取的 Proxy 陣列,其結果則為選取的 Proxy。 Proxy toString() 方法是用來產生要對使用者顯示的清單。 Proxy 的 toString() 方法會產生 Proxy 位置欄位的影像,亦即 Resource.location().string()。

因為顯示的是 Proxy 的位置,所以我們需要確定那是對使用者友善的 Proxy 位置,也就是包含分段路徑名稱而非資料庫 ID。 伺服器可在其傳回的 Proxy 中自由使用任何形式的位置。 一般來說,如果 Proxy 是用來返回到伺服器的話,則會選取最有處理效率的格式。 最有效率的格式很少是對使用者友善的。 在任何情況下,用戶端都不應該假定要使用的位置形式。 因此,當我們要求資料庫清單及查詢清單時,我們也會要求該清單上每一個項目的 USER_FRIENDLY_LOCATION 內容。 然後,在 setUserFriendlyLocation 方法中,我們會以對使用者友善的版本來修改每一個 Proxy 的位置。

此應用程式會忽略選取的查詢定義動態過濾器(也稱為查詢參數)的可能性,並顯示奇怪的行為,如果選取的查詢具有動態過濾器,此應用程式可能會失敗。 更健全的實作會向伺服器要求查詢的 DYNAMIC_FILTERS 內容,並在執行該查詢之前向使用者取得遺漏的資料。 這留待讀者自行練習。

請注意,當 CqRowData 物件放入陣列中以顯示時,會在每一列呼叫 CqRowData.getValues()。 這是必要的動作,因為在釋放 CqResultSet 疊代子之後無法使用以 Java™ 物件計算列資料值時所需的資訊,而這種情況會在此疊代子到達結尾時自動發生。

本範例未使用 ExecuteQuery.showResults(指名的檢視器)的第二個參數,但下一個範例會使用它,以容許使用者選取一列結果集並顯示相關聯的記錄。

說明或示範剛剛所遵循的步驟的結果。

課程檢查點

既然您已學會如何使用 Rational CM API 來執行查詢,您就可以自行檢閱可用的 Rational CM API 介面,及探索如何撰寫或修改現有查詢。
在這一課,您學到如何使用 Rational CM API 來執行查詢。您學到下列各項:
  • 關於執行 Rational ClearQuest 查詢所需的 Rational CM API 物件。
  • 如何重覆使用結果集。
  • 如何建立執行查詢的用戶端應用程式。

意見
< 上一個課程 | 下一個課程 >