MyBatis 調用 MySQL 存儲過程完整範例(含 IN/OUT 參數映射)
一、MySQL 存儲過程定義
DELIMITER $$
USE `test`$$
DROP PROCEDURE IF EXISTS `user_user_selectUserByProAndCity_sp`$$
CREATE DEFINER=`demao`@`%` PROCEDURE `user_user_selectUserByProAndCity_sp`(
IN provinceId INT,
IN cityId INT,
IN pageIndex INT,
IN pageSize INT
)
BEGIN
SELECT
*,
(SELECT COUNT(*) FROM user_user_t
WHERE province_id LIKE CONCAT('%', provinceId, '%')
AND city_id LIKE CONCAT('%', cityId, '%')
) AS userCount
FROM user_user_t
WHERE province_id LIKE CONCAT('%', provinceId, '%')
AND city_id LIKE CONCAT('%', cityId, '%')
LIMIT pageIndex, pageSize;
END$$
DELIMITER ;
二、MyBatis Mapper XML 調用
<select id="selectUserByProvinceAndCity"
resultMap="BaseResultMap"
statementType="CALLABLE">
{CALL user_user_selectUserByProAndCity_sp(
#{provinceId, mode=IN, jdbcType=INTEGER},
#{cityId, mode=IN, jdbcType=INTEGER},
#{pageIndex, mode=IN, jdbcType=INTEGER},
#{pageSize, mode=IN, jdbcType=INTEGER}
)}
</select>
三、關鍵配置解析
| 屬性 | 值 | 說明 |
| statementType | CALLABLE | 告訴 MyBatis 使用 CallableStatement 執行存儲過程 |
| mode=IN | 輸入參數 | Java → 資料庫 |
| mode=OUT | 輸出參數 | 資料庫 → Java(需配合 jdbcType、resultMap 或 @Options) |
| mode=INOUT | 雙向參數 | 輸入並接收返回值 |
四、帶 OUT 參數的完整範例
存儲過程
CREATE PROCEDURE sp_get_user_count(
IN p_dept_id INT,
OUT p_total INT
)
BEGIN
SELECT COUNT(*) INTO p_total FROM sys_user WHERE dept_id = p_dept_id;
END
Mapper
Java 介面
調用
五、常見坑點與最佳實踐
- 結果集處理:MySQL 存儲過程可返回多個結果集,MyBatis 3.5+ 支援
resultSets 屬性映射多 ResultMap
- 參數名稱:
#{paramName} 需與 Java 方法參數名一致(或用 @Param 指定)
- JDBC 驅動版本:MySQL Connector/J 5.1.47+ / 8.0+ 穩定支援 CALLABLE
- 事務邊界:存儲過程內若有 DML,外層事務管理需謹慎(建議存儲過程內自行控制事務)
- 效能考量:簡單 CRUD 優先用 MyBatis 動態 SQL,存儲過程適合複雜邏輯下推、跨表原子操作、減少網路往返場景