Skip to content

Spring Integration JDBC 存储过程教程

本教程专为 Spring 初学者设计,通过 Kotlin 代码示例和现代注解配置,系统讲解存储过程组件的使用场景、配置方法和最佳实践。


一、为什么需要存储过程组件?

当遇到以下场景时,简单的 JDBC 支持可能不足:

  1. 遗留数据库:需兼容旧有数据库设计
  2. 复杂数据处理:涉及多步骤业务逻辑
  3. 性能优化:通过预编译 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 示例进一步实践。