前面介紹了通過JDBC如何管理資料庫,當時提到Statement專門提供了executeQuery方法用於查詢操作,為什麼查詢操作這麼特殊呢?這是因為其它語句跑完一次就了事了,頂多像insert、update、delete再返回受影響的記錄數量,但select命令跟它們不一樣,查詢語句可能會返回多條 ...
前面介紹了通過JDBC如何管理資料庫,當時提到Statement專門提供了executeQuery方法用於查詢操作,為什麼查詢操作這麼特殊呢?這是因為其它語句跑完一次就了事了,頂多像insert、update、delete再返回受影響的記錄數量,但select命令跟它們不一樣,查詢語句可能會返回多條記錄,每條記錄又包含多個欄位。似此多條記錄多個欄位的情景,返回值無論定義為哪種類型都不太好辦,故而乾脆給個單獨的executeQuery方法,該方法的返回值也設置成專屬的ResultSet類型,表示查詢方法返回了一個結果集,詳細的記錄結果請到結果集中遍歷獲得。
據此可將記錄查詢的操作過程分成以下四個步驟:
1、獲取資料庫連接:該步驟調用DriverManager類的getConnection方法獲得連接對象。
2、創建該連接的執行報告:該步驟調用Connection對象的createStatement方法獲得執行報告。
3、命令報告執行查詢語句:該步驟調用報告對象的executeQuery方法來執行查詢語句,並返回查詢記錄的結果集。
4、迴圈遍歷結果集裡面的所有記錄:通常該步驟需調用結果集對象的next方法不斷往後遍歷,也就是將結果集的指示游標一步一步向後移動。在遍歷過程當中,可能要調用結果集對象的其它方法進一步操作,ResultSet的常見方法分成三類,說明如下:
1、移動游標
該類方法可將當前游標移動到指定位置,主要包括下列方法:
next:將游標移到後一條記錄。該方法返回true表示尚未移到末尾,返回false表示已經移到末尾。
absolute:將游標移到第幾條記錄,如果參數為負數則表示倒數的第幾條。
first:將游標移到第一條記錄。
last:將游標移到最後一條記錄。
previous:將游標移到前一條記錄。
beforeFirst:將游標移到第一條記錄之前。
afterLast:將游標移到最後一條記錄之後。
2、判斷游標位置
該類方法可判斷當前游標是否處於某個位置,主要包括下列方法:
isFirst:游標是否指向第一條記錄。
isLast:游標是否指向最後一條記錄。
isBeforeFirst:游標是否在第一條記錄之前。
isAfterLast:游標是否在最後一條記錄之後。
3、從當前游標獲取數據
該類方法可從當前游標指向的記錄中獲取欄位值,當方法參數為整型時,表示獲取指定序號的欄位值;當方法參數為字元串時,表示獲取指定名稱的欄位值。相關的獲取方法羅列如下:
getInt:獲取指定序號或者指定名稱的欄位整型值。
getLong:獲取指定序號或者指定名稱的欄位長整值。
getFloat:獲取指定序號或者指定名稱的欄位浮點值。
getDouble:獲取指定序號或者指定名稱的欄位雙精度值。
getString:獲取指定序號或者指定名稱的欄位字元串值。
getDate:獲取指定序號或者指定名稱的欄位日期值。
接下來舉幾個具體應用的例子,首先要從teacher表中查詢所有記錄,則依次連接資料庫、創建連接的報告、執行查詢語句,再迴圈遍歷結果集獲取每條記錄的欄位信息。這一連串的查詢代碼示例如下:
// 查詢所有記錄(預設排序) private static void showAllRecord() { String sql = "select * from teacher"; // 連接資料庫、創建連接的報告、執行查詢語句 try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql)) { while (rs.next()) { // 迴圈遍歷結果集裡面的所有記錄 int gonghao = rs.getInt("gonghao"); // 獲取指定欄位的整型值 String name = rs.getString("name"); // 獲取指定欄位的字元串值 Date birthday = rs.getDate("birthday"); // 獲取指定欄位的日期值 int sex = rs.getInt("sex"); // 獲取指定欄位的整型值 String course = rs.getString("course"); // 獲取指定欄位的字元串值 String desc = String.format("工號為%d,姓名為%s,出生日期為%s,性別為%s,任教課程為%s。", gonghao, name, getFormatDate(birthday), sex==0 ? "男性" : "女性", course); System.out.println("當前教師信息為:"+desc); } } catch (SQLException e) { e.printStackTrace(); } }
註意MySQL未提供將日期轉成字元串的to_char函數,因而只能先取到Date類型的欄位值,再將其通過Java代碼轉為字元串。日期類型轉換為字元串類型的方法代碼如下所示:
// 獲取指定格式的日期字元串 public static String getFormatDate(Date date) { // 創建一個日期格式化的工具 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); // 將當前日期時間按照指定格式輸出格式化後的日期時間字元串 return sdf.format(date); }
接著運行上面的查詢方法showAllRecord,觀察到日誌視窗完整輸出瞭如下的五條記錄信息。
當前教師信息為:工號為1,姓名為張老師,出生日期為1983-03-03,性別為女性,任教課程為語文。 當前教師信息為:工號為2,姓名為李老師,出生日期為1984-04-04,性別為男性,任教課程為數學。 當前教師信息為:工號為3,姓名為王老師,出生日期為1985-05-05,性別為女性,任教課程為英語。 當前教師信息為:工號為4,姓名為趙老師,出生日期為1986-06-06,性別為男性,任教課程為物理。 當前教師信息為:工號為5,姓名為劉老師,出生日期為1987-07-07,性別為女性,任教課程為化學。
然後在原語句增加排序條件,讓所有記錄按照生日欄位降序排列,則修改後的查詢代碼如下所示:
// 查詢所有記錄(按照生日倒序) private static void showAllRecordByBirthday() { String sql = "select * from teacher order by birthday desc"; // 連接資料庫、創建連接的報告、執行查詢語句 try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql)) { while (rs.next()) { // 迴圈遍歷結果集裡面的所有記錄 int gonghao = rs.getInt("gonghao"); // 獲取指定欄位的整型值 String name = rs.getString("name"); // 獲取指定欄位的字元串值 Date birthday = rs.getDate("birthday"); // 獲取指定欄位的日期值 int sex = rs.getInt("sex"); // 獲取指定欄位的整型值 String course = rs.getString("course"); // 獲取指定欄位的字元串值 String desc = String.format("工號為%d,姓名為%s,出生日期為%s,性別為%s,任教課程為%s。", gonghao, name, getFormatDate(birthday), sex==0 ? "男性" : "女性", course); System.out.println("當前教師信息為:"+desc); } } catch (SQLException e) { e.printStackTrace(); } }
增加了showAllRecordByBirthday方法之後,再次運行測試程式,從日誌視窗可見這次的記錄結果以生日欄位降序顯示了。
當前教師信息為:工號為5,姓名為劉老師,出生日期為1987-07-07,性別為女性,任教課程為化學。 當前教師信息為:工號為4,姓名為趙老師,出生日期為1986-06-06,性別為男性,任教課程為物理。 當前教師信息為:工號為3,姓名為王老師,出生日期為1985-05-05,性別為女性,任教課程為英語。 當前教師信息為:工號為2,姓名為李老師,出生日期為1984-04-04,性別為男性,任教課程為數學。 當前教師信息為:工號為1,姓名為張老師,出生日期為1983-03-03,性別為女性,任教課程為語文。
排序條件僅僅調整返回記錄的順序,然而分組條件就不一樣了。因為分組條件存在統計操作,像count、sum、max這些函數只返回運算結果,但從結果集中取數據有賴於欄位名稱,所以需要在統計函數之後加個別名,相當於該函數的運算結果暫存於該別名變數。比如表達式“count(sex) count”說的就是計數結果以count命名,游標從count欄位獲取到的即為計數值。下麵是對teacher表按照性別欄位分組統計的查詢代碼例子:
// 查詢性別分組。註意要給count之類的函數結果分配別名 private static void showRecordGroupBySex() { String sql = "select sex,count(sex) count from teacher group by sex order by sex asc"; // 連接資料庫、創建連接的報告、執行查詢語句 try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql)) { while (rs.next()) { // 迴圈遍歷結果集裡面的所有記錄 int sex = rs.getInt("sex"); // 獲取指定欄位的整型值 int count = rs.getInt("count"); // 獲取指定欄位的整型值 String desc = String.format("%s老師有%d位;", sex==0 ? "男" : "女", count); System.out.print(desc); } } catch (SQLException e) { e.printStackTrace(); } }
運行包含showRecordGroupBySex方法的測試程式,果然正確輸出了預期的統計日誌如下所示。
男老師有2位;女老師有3位;
更多Java技術文章參見《Java開發筆記(序)章節目錄》