萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 資料庫 >> MySQL 教學

MySQL 儲存過程實戰:多結果集返回與 Java 呼叫範例

MySQL 自定義函數(FUNCTION)僅能返回單一標量值,需返回多個結果集時必須使用儲存過程。本文展示一個接受兩條 SQL、返回兩個結果集的儲存過程,並給出完整 Java (JDBC) 呼叫代碼。

儲存過程定義

DROP PROCEDURE IF EXISTS `testguocheng`;

CREATE PROCEDURE `testguocheng`(
    IN `sql1` VARCHAR(100),
    IN `sql2` VARCHAR(100)
)
BEGIN
    -- 第一個結果集
    SET @SQLStr1 = CONCAT(sql1);
    PREPARE stmt1 FROM @SQLStr1;
    EXECUTE stmt1;
    DEALLOCATE PREPARE stmt1;

    -- 第二個結果集
    SET @SQLStr2 = CONCAT(sql2);
    PREPARE stmt2 FROM @SQLStr2;
    EXECUTE stmt2;
    DEALLOCATE PREPARE stmt2;
END;

關鍵點

  • 使用預處理語句動態執行傳入的 SQL 字串。
  • 每個 EXECUTE 產生一個結果集,JDBC 依序讀取。
  • 執行完畢需 DEALLOCATE PREPARE 釋放準備句柄。

Java 呼叫代碼

public static void getData() {
    Connection conn = null;
    CallableStatement stmt = null;
    ResultSet rs = null;

    try {
        conn = JdbcUtils.getConnection();  // 自行實現的連線池工具
        // 呼叫儲存過程,傳入兩條 SQL
        String callSql = "{call testguocheng('SELECT * FROM tsys_user;', 'SELECT COUNT(*) FROM tsys_user;')}";
        stmt = conn.prepareCall(callSql);

        boolean hasResult = stmt.execute();
        int resultSetIndex = 0;

        while (hasResult) {
            resultSetIndex++;
            System.out.println("=== 第 " + resultSetIndex + " 個結果集 ===");

            rs = stmt.getResultSet();
            if (rs != null) {
                if (resultSetIndex == 1) {
                    // 第一個結果集:用戶明細
                    while (rs.next()) {
                        System.out.println("uid: " + rs.getString("uid") +
                                           ", uname: " + rs.getString("uname"));
                    }
                } else {
                    // 第二個結果集:聚合統計
                    while (rs.next()) {
                        System.out.println("總數:" + rs.getString(1));
                    }
                }
                rs.close();
            }

            // 移動到下一個結果集
            hasResult = stmt.getMoreResults();
        }
    } catch (Exception e) {
        System.err.println("執行異常: " + e.getMessage());
        e.printStackTrace();
    } finally {
        // 資源釋放順序:ResultSet → Statement → Connection
        try { if (rs != null) rs.close(); } catch (Exception ignore) {}
        try { if (stmt != null) stmt.close(); } catch (Exception ignore) {}
        try { if (conn != null) conn.close(); } catch (Exception ignore) {}
    }
}

JDBC 多結果集處理口訣

  1. stmt.execute() 返回第一個結果集是否存在。
  2. stmt.getResultSet() 取得當前結果集。
  3. 處理完 rs.close()。
  4. stmt.getMoreResults() 移至下一個結果集,返回 true 繼續迴圈。

進階:參數化動態 SQL(防注入)

上述範例直接拼接 SQL 字串,存在注入風險。生產環境建議:

CREATE PROCEDURE `safe_proc`(IN tbl VARCHAR(64), IN min_id INT)
BEGIN
    SET @sql = CONCAT('SELECT * FROM ', QUOTENAME(tbl), ' WHERE id > ?');
    PREPARE stmt FROM @sql;
    SET @p = min_id;
    EXECUTE stmt USING @p;
    DEALLOCATE PREPARE stmt;
END

Java 端呼叫:{call safe_proc('tsys_user', ?)} 並 stmt.setInt(1, 100)。

總結

儲存過程 + PREPARE/EXECUTE 實現動態多結果集返回;JDBC 端以 getMoreResults() 迴圈消費。務必做好參數化、資源釋放與異常處理,構建健壯的資料存取層。

copyright © 萬盛學電腦網 all rights reserved