Appearance
Spring Integration JDBC 存储过程教程
本教程专为 Spring 初学者设计,通过 Kotlin 代码示例和现代注解配置,系统讲解存储过程组件的使用场景、配置方法和最佳实践。
一、为什么需要存储过程组件?
当遇到以下场景时,简单的 JDBC 支持可能不足:
- 遗留数据库:需兼容旧有数据库设计
- 复杂数据处理:涉及多步骤业务逻辑
- 性能优化:通过预编译 SQL 提升效率
Spring Integration 提供三种存储过程组件:
二、支持数据库清单
存储过程支持 | 存储函数支持 |
---|---|
✅ Apache Derby | ✅ MySQL |
✅ DB2 | ✅ Microsoft SQL Server |
✅ MySQL | ✅ Oracle |
✅ Microsoft SQL Server | ✅ PostgreSQL |
✅ Oracle | |
✅ PostgreSQL | |
✅ Sybase |
CAUTION
非完全支持数据库注意事项
即使数据库不在官方支持列表(如 H2),只要支持存储过程/函数仍可使用组件,但需严格测试参数映射和返回值处理。
三、核心配置属性详解
通过 @Bean
定义组件时需关注以下关键属性:
kotlin
@Bean
fun storedProcOutboundGateway(
dataSource: DataSource
) = StoredProcOutboundGateway().apply {
setDataSource(dataSource)
storedProcedureName = "CALCULATE_ORDER"
isFunction = false
ignoreColumnMetaData = false
sqlParameterSourceFactory = BeanPropertySqlParameterSourceFactory()
usePayloadAsParameterSource = true
}
属性 | 说明 | 默认值 |
---|---|---|
storedProcedureName | 存储过程/函数名 | 必填 |
isFunction | 是否调用函数 | false |
ignoreColumnMetaData | 禁用元数据自动检索 | false |
usePayloadAsParameterSource | 使用消息体作为参数源 | true |
四、参数映射策略
1. 自动映射(简单场景)
kotlin
data class OrderRequest(val productId: Int, val quantity: Int)
// 自动将 OrderRequest 属性映射为存储过程参数
outboundGateway.usePayloadAsParameterSource = true
WARNING
属性名大小写问题
自动映射要求实体类属性名必须全小写(如 productid
),因为数据库元数据会转为小写匹配。若需使用驼峰命名,请采用显式参数声明。
2. 显式声明(复杂场景)
kotlin
outboundGateway.apply {
addParameter(SqlParameter("productId", Types.INTEGER))
addParameter(SqlOutParameter("totalPrice", Types.DECIMAL))
addParameter(SqlReturnResultSet("items", OrderRowMapper()))
}
五、组件类型与使用场景
1. 入站通道适配器 (Inbound Channel Adapter)
定时轮询存储过程,将结果转为消息
kotlin
@Bean
fun storedProcInboundAdapter(dataSource: DataSource) =
StoredProcPollingChannelAdapter(
StoredProcExecutor(dataSource).apply {
storedProcedureName = "FETCH_NEW_ORDERS"
addReturnResultSet("orders", OrderRowMapper())
}
).apply {
setOutputChannelName("orderProcessingChannel")
setPoll( Pollers.fixedRate(5000).maxMessagesPerPoll(1) )
}
2. 出站通道适配器 (Outbound Channel Adapter)
单向调用存储过程,不等待返回值
kotlin
@Bean
fun storedProcOutboundAdapter(dataSource: DataSource) =
StoredProcMessageHandler(StoredProcExecutor(dataSource).apply {
storedProcedureName = "LOG_OPERATION"
}).apply {
setOutputChannel( NullChannel() ) 无需返回通道
}
3. 出站网关 (Outbound Gateway)
调用存储过程并返回结果
kotlin
@Bean
fun orderCalcGateway(dataSource: DataSource) =
StoredProcOutboundGateway(StoredProcExecutor(dataSource).apply {
storedProcedureName = "CALCULATE_ORDER_TOTAL"
addParameter(SqlParameter("orderId", Types.INTEGER))
addSqlOutParameter(SqlOutParameter("total", Types.DECIMAL))
})
java
// 传统 Java 配置(不推荐)
public StoredProcOutboundGateway gateway(DataSource dataSource) {
StoredProcExecutor executor = new StoredProcExecutor(dataSource);
executor.setStoredProcedureName("CALCULATE_ORDER_TOTAL");
executor.addParameter(new SqlParameter("orderId", Types.INTEGER));
executor.addSqlOutParameter(new SqlOutParameter("total", Types.DECIMAL));
return new StoredProcOutboundGateway(executor);
}
六、实战示例:订单处理系统
场景描述
- 存储过程
PROCESS_ORDER
接收订单 ID - 返回订单详情和计算后的总价
Kotlin 实现
kotlin
// 网关配置
@Bean
fun orderProcessingGateway(dataSource: DataSource) =
StoredProcOutboundGateway(StoredProcExecutor(dataSource).apply {
storedProcedureName = "PROCESS_ORDER"
addParameter(SqlParameter("order_id", Types.INTEGER))
addReturnResultSet("details", OrderDetailMapper())
addSqlOutParameter(SqlOutParameter("total", Types.DECIMAL))
}).apply {
setOutputChannelName("orderResultChannel")
}
// 结果处理器
@Bean
fun orderResultHandler() = MessageHandler { message ->
val result = message.payload as Map<String, Any>
val details = result["details"] as List<OrderDetail>
val total = result["total"] as BigDecimal
println("订单明细: $details")
println("实付金额: $total")
}
七、常见问题解决方案
❌ 问题1:参数绑定失败
log
Parameter 'productName' not found in parameter list
✅ 解决方案:
- 确认数据库元数据检索是否开启:
ignoreColumnMetaData=false
- 显式声明参数:kotlin
executor.addParameter(SqlParameter("product_name", Types.VARCHAR))
❌ 问题2:返回结果集解析异常
log
Column 'PRICE' not found in result set
✅ 解决方案:
- 检查
RowMapper
字段映射 - 使用别名统一大小写:sql
CREATE PROCEDURE GET_ITEMS() BEGIN SELECT product_name AS productname, unit_price AS price -- FROM items; END
❌ 问题3:函数调用错误
log
SQL state [99999]; Invalid function call
✅ 解决方案:
- 启用函数调用模式:kotlin
executor.isFunction = true
通过本教程,您已掌握在 Spring Integration 中安全高效地调用存储过程的核心技能。建议结合 Spring Integration Samples 中的 Derby 示例进一步实践。