MyBatis 作為主流持久層框架,支援透過 statementType="CALLABLE" 調用資料庫儲存過程。本文從儲存過程基礎概念、優缺點分析、MySQL 語法,到 MyBatis 完整整合範例(含 XML 配置、Mapper 接口、實體類、測試代碼),手把手教你落地實作。
儲存過程是一組為完成特定功能的 SQL 語句集,經編譯後儲存在資料庫中。用戶透過名稱與參數調用執行。它可視為資料庫層面的「可程式函數」,封裝業務邏輯,支援流程控制、變數宣告等進階特性。
EXECUTE 權限控制資料存取,避免直接暴露資料表。CREATE PROCEDURE sp_name()
BEGIN
-- SQL 語句
END;
CALL sp_name(); -- 必須加括號,即使無參數
DROP PROCEDURE sp_name;
SHOW PROCEDURE STATUS; -- 列出所有儲存過程基本資訊
SHOW CREATE PROCEDURE sp_name; -- 顯示特定儲存過程完整定義
DROP TABLE IF EXISTS `cus_device`;
CREATE TABLE `cus_device` (
`device_sn` VARCHAR(20) NOT NULL COMMENT '設備編號',
`device_cat_id` INT(1) DEFAULT NULL COMMENT '設備類型',
`device_name` VARCHAR(64) DEFAULT NULL COMMENT '設備名稱',
`device_type` VARCHAR(64) DEFAULT NULL COMMENT '設備型號',
PRIMARY KEY (`device_sn`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP PROCEDURE IF EXISTS `countDevicesName`;
DELIMITER ;;
CREATE PROCEDURE `countDevicesName`(
IN dName VARCHAR(12),
OUT deviceCount INT
)
BEGIN
SELECT COUNT(*) INTO deviceCount FROM cus_device WHERE device_name = dName;
END ;;
DELIMITER ;
說明:IN 為輸入參數,OUT 為輸出參數。DELIMITER 變更語句結束符,避免過程內部 ; 衝突。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<typeAliases>
<typeAlias type="com.example.DevicePOJO" alias="DevicePOJO"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/bms?characterEncoding=GBK"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/example/DeviceMapper.xml"/>
</mappers>
</configuration>
public class DevicePOJO {
private String deviceName; // 對應 IN 參數
private Integer deviceCount; // 對應 OUT 參數,建議用包裝類型
// getter/setter 略
}
public interface DeviceDAO {
void count(DevicePOJO devicePOJO);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.DeviceDAO">
<select id="count" parameterType="DevicePOJO" statementType="CALLABLE">
<![CDATA[
CALL countDevicesName(
#{deviceName, mode=IN, jdbcType=VARCHAR},
#{deviceCount, mode=OUT, jdbcType=INTEGER}
);
]]>
</select>
</mapper>
核心細節:
statementType="CALLABLE":告知 MyBatis 執行儲存過程,缺少會拋出 PersistenceException。mode=IN/OUT:對應儲存過程參數方向。jdbcType:需與資料庫欄位類型對應。public class TestProcedure {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml";
Reader reader = Resources.getResourceAsReader(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
SqlSession session = sqlSessionFactory.openSession();
DeviceDAO dao = session.getMapper(DeviceDAO.class);
DevicePOJO device = new DevicePOJO();
device.setDeviceName("測試設備");
dao.count(device);
System.out.println("設備總數:" + device.getDeviceCount());
session.close();
}
}

透過 CALLABLE 模式,MyBatis 能優雅地整合資料庫儲存過程,適合將高效能、高安全性的核心邏輯下沉至資料庫層。實務上建議:簡單 CRUD 用 MyBatis XML/註解,複雜報表/批次運算考慮儲存過程,並做好版本控管與文檔。