跳到主要内容
版本:7.0.2

存储过程

DeepSeek V3 中英对照 Stored Procedures

在某些情况下,仅靠普通的 JDBC 支持是不够的。您可能需要处理遗留的关系型数据库模式,或者有复杂的数据处理需求,但最终不得不使用存储过程或存储函数。自 Spring Integration 2.1 起,我们提供了三个组件来执行存储过程或存储函数:

  • 存储过程入站通道适配器

  • 存储过程出站通道适配器

  • 存储过程出站网关

支持的数据库

为了支持对存储过程和存储函数的调用,存储过程组件使用了 org.springframework.jdbc.core.simple.SimpleJdbcCall 类。因此,以下数据库在执行存储过程时得到了完全支持:

  • Apache Derby

  • DB2

  • MySQL

  • Microsoft SQL Server

  • Oracle

  • PostgreSQL

  • Sybase

如果您希望执行存储函数,以下数据库已获得全面支持:

  • MySQL

  • Microsoft SQL Server

  • Oracle

  • PostgreSQL

备注

尽管您的特定数据库可能未得到完全支持,但只要您的 RDBMS 支持存储过程或存储函数,您仍然可以非常成功地使用 Spring Integration 的存储过程组件。

事实上,一些提供的集成测试使用了 H2 数据库。尽管如此,对这些使用场景进行彻底测试仍然非常重要。

配置

存储过程组件提供完整的XML命名空间支持,其配置方式与先前讨论的通用JDBC组件相同。

通用配置属性

所有存储过程组件共享特定的配置参数:

  • auto-startup:生命周期属性,指示此组件是否应在应用程序上下文启动期间启动。默认值为 true。可选。

  • data-source:对 javax.sql.DataSource 的引用,用于访问数据库。必需。

  • id:标识底层的 Spring bean 定义,该定义是 EventDrivenConsumerPollingConsumer 的实例,具体取决于出站通道适配器的 channel 属性引用的是 SubscribableChannel 还是 PollableChannel。可选。

  • ignore-column-meta-data:对于完全支持的数据库,底层的 SimpleJdbcCall 类可以从 JDBC 元数据中自动检索存储过程或存储函数的参数信息。

    但是,如果数据库不支持元数据查找,或者您需要提供自定义的参数定义,则可以将此标志设置为 true。默认值为 false。可选。

  • is-function:如果为 true,则调用 SQL 函数。在这种情况下,stored-procedure-namestored-procedure-name-expression 属性定义被调用函数的名称。默认值为 false。可选。

  • stored-procedure-name:此属性指定存储过程的名称。如果 is-function 属性设置为 true,则此属性指定函数名称。必须指定此属性或 stored-procedure-name-expression

  • stored-procedure-name-expression:此属性通过使用 SpEL 表达式指定存储过程的名称。通过使用 SpEL,您可以访问完整的消息(如果可用),包括其标头和有效负载。您可以使用此属性在运行时调用不同的存储过程。例如,您可以提供要作为消息标头执行的存储过程名称。表达式必须解析为 String

    如果 is-function 属性设置为 true,则此属性指定存储函数。必须指定此属性或 stored-procedure-name

  • jdbc-call-operations-cache-size:定义缓存的 SimpleJdbcCallOperations 实例的最大数量。基本上,对于每个存储过程名称,都会创建一个新的 SimpleJdbcCallOperations 实例,该实例随后被缓存。

    备注

    Spring Integration 2.2 添加了 stored-procedure-name-expression 属性和 jdbc-call-operations-cache-size 属性。

    默认缓存大小为 10。值为 0 表示禁用缓存。不允许使用负值。

    如果启用 JMX,有关 jdbc-call-operations-cache 的统计信息将作为 MBean 公开。有关更多信息,请参阅 MBean Exporter

  • sql-parameter-source-factory:(不适用于存储过程入站通道适配器。)对 SqlParameterSourceFactory 的引用。默认情况下,通过使用 BeanPropertySqlParameterSourceFactory,传入 Message 有效负载的 bean 属性将用作存储过程输入参数的源。

    这对于基本用例可能已足够。对于更复杂的选项,请考虑传入一个或多个 ProcedureParameter 值。请参阅 定义参数源。可选。

  • use-payload-as-parameter-source:(不适用于存储过程入站通道适配器。)如果设置为 true,则 Message 的有效负载将用作提供参数的源。但是,如果设置为 false,则整个 Message 可用作参数的源。

    如果未传入过程参数,则此属性默认为 true。这意味着,通过使用默认的 BeanPropertySqlParameterSourceFactory,有效负载的 bean 属性将用作存储过程或存储函数的参数值的源。或者,从版本 6.5 开始,如果提及的有效负载是 Map,则作为键。

    但是,如果传入了过程参数,则此属性(默认情况下)评估为 falseProcedureParameter 允许提供 SpEL 表达式。因此,能够访问整个 Message 是非常有益的。此属性设置在底层的 StoredProcExecutor 上。可选。

通用配置子元素

存储过程组件共享一组通用的子元素,可用于定义并向存储过程或存储函数传递参数。以下元素可供使用:

  • parameter
  • returning-resultset
  • sql-parameter-definition
  • poller
  • parameter: 提供一种机制来为存储过程提供参数。参数可以是静态的,也可以通过 SpEL 表达式提供。
    <int-jdbc:parameter name=""         // <1>
    type="" // <2>
    value=""/> // <3>

    <int-jdbc:parameter name=""
    expression=""/> // <4>
    • 要传递给存储过程或存储函数的参数名称。必需。

    • 此属性指定值的类型。如果未提供,则此属性默认为 java.lang.String。仅当使用 value 属性时才使用此属性。可选。

    • 参数的值。必须提供此属性或 expression 属性之一。可选。

    • 可以指定一个 SpEL 表达式来传递参数值,以替代 value 属性。如果指定了 expression,则不允许使用 value 属性。可选。

  • returning-resultset: 存储过程可能返回多个结果集。通过设置一个或多个 returning-resultset 元素,可以指定 RowMappers 来将每个返回的 ResultSet 转换为有意义的对象。可选。
    <int-jdbc:returning-resultset name="" row-mapper="" />
  • sql-parameter-definition: 如果使用完全受支持的数据库,通常不需要指定存储过程参数定义。相反,这些参数可以从 JDBC 元数据自动派生。但是,如果使用不完全受支持的数据库,则必须使用 sql-parameter-definition 元素显式设置这些参数。 也可以通过使用 ignore-column-meta-data 属性来关闭对通过 JDBC 获得的参数元数据信息的任何处理。
    <int-jdbc:sql-parameter-definition
    name="" // <1>
    direction="IN" // <2>
    type="STRING" // <3>
    scale="5" // <4>
    type-name="FOO_STRUCT" // <5>
    return-type="fooSqlReturnType"/> // <6>
  • 指定 SQL 参数的名称。必需。

  • 指定 SQL 参数定义的方向。默认为 IN。有效值为:INOUTINOUT。如果过程返回结果集,请使用 returning-resultset 元素。可选。

  • 用于此 SQL 参数定义的 SQL 类型。转换为整数值,如 java.sql.Types 所定义。或者,也可以提供整数值。如果未显式设置此属性,则默认为 'VARCHAR'。可选。

  • SQL 参数的小数位数。仅用于数值和十进制参数。可选。

  • 用户命名类型的 typeName,例如:STRUCTDISTINCTJAVA_OBJECT 和命名数组类型。此属性与 scale 属性互斥。可选。

  • 对复杂类型的自定义值处理器的引用。一个 SqlReturnType 的实现。此属性与 scale 属性互斥,并且仅适用于 OUT 和 INOUT 参数。可选。

  • poller: 如果此端点是 PollingConsumer,则允许配置消息轮询器。可选。

定义参数源

参数源负责管理从Spring Integration消息属性中检索并映射到相关存储过程输入参数的技术。

存储过程组件遵循特定规则。默认情况下,Message 负载的 bean 属性将用作存储过程输入参数的来源。在这种情况下,会使用 BeanPropertySqlParameterSourceFactory。对于基本用例而言,这可能已经足够。以下示例说明了这种默认行为。

important

为了让通过使用 BeanPropertySqlParameterSourceFactory 自动查找 bean 属性的功能正常工作,你的 bean 属性必须定义为小写形式。这是因为在 org.springframework.jdbc.core.metadata.CallMetaDataContext(具体方法是 matchInParameterValuesWithCallParameters())中,检索到的存储过程参数声明会被转换为小写。因此,如果你有驼峰命名的 bean 属性(例如 lastName),查找将会失败。在这种情况下,请显式提供一个 ProcedureParameter

假设我们有一个包含以下三个属性的简单Bean作为有效载荷:idnamedescription。此外,我们有一个名为 INSERT_COFFEE 的简单存储过程,它接受三个输入参数:idnamedescription。同时,我们使用一个完全支持的数据库。在这种情况下,以下存储过程出站适配器的配置就足够了:

<int-jdbc:stored-proc-outbound-channel-adapter data-source="dataSource"
channel="insertCoffeeProcedureRequestChannel"
stored-procedure-name="INSERT_COFFEE"/>

对于更复杂的选项,可以考虑传入一个或多个 ProcedureParameter 值。

如果你确实显式提供了 ProcedureParameter 值,默认情况下,系统会使用 ExpressionEvaluatingSqlParameterSourceFactory 进行参数处理,以充分发挥 SpEL 表达式的强大功能。

如果你需要对参数获取方式进行更精细的控制,可以考虑通过 sql-parameter-source-factory 属性传入自定义的 SqlParameterSourceFactory 实现。

存储过程入站通道适配器

以下清单列出了存储过程入站通道适配器的重要属性:

<int-jdbc:stored-proc-inbound-channel-adapter
channel="" // <1>
stored-procedure-name=""
data-source=""
auto-startup="true"
id=""
ignore-column-meta-data="false"
is-function="false"
skip-undeclared-results="" // <2>
return-value-required="false" // <3>
<int:poller/>
<int-jdbc:sql-parameter-definition name="" direction="IN"
type="STRING"
scale=""/>
<int-jdbc:parameter name="" type="" value=""/>
<int-jdbc:parameter name="" expression=""/>
<int-jdbc:returning-resultset name="" row-mapper="" />
</int-jdbc:stored-proc-inbound-channel-adapter>
  • 轮询消息发送到的通道。如果存储过程或函数不返回任何数据,Message 的有效载荷为 null。必需。

  • 如果此属性设置为 true,则绕过存储过程调用中所有没有对应 SqlOutParameter 声明的结果。例如,即使您的存储过程只声明了一个结果参数,存储过程也可能返回一个更新计数值。具体行为取决于数据库实现。该值设置在底层的 JdbcTemplate 上。默认值为 true。可选。

  • 指示是否应包含此过程的返回值。自 Spring Integration 3.0 起。可选。

存储过程出站通道适配器

以下清单列出了存储过程出站通道适配器的重要属性:

<int-jdbc:stored-proc-outbound-channel-adapter channel=""                        // <1>
stored-procedure-name=""
data-source=""
auto-startup="true"
id=""
ignore-column-meta-data="false"
order="" // <2>
sql-parameter-source-factory=""
use-payload-as-parameter-source="">
<int:poller fixed-rate=""/>
<int-jdbc:sql-parameter-definition name=""/>
<int-jdbc:parameter name=""/>

</int-jdbc:stored-proc-outbound-channel-adapter>
  • 此端点的接收消息通道。必需。

  • 指定当此端点作为订阅者连接到通道时的调用顺序。这在通道使用 failover 分发策略时尤其重要。当此端点本身是带有队列的通道的轮询消费者时,此设置无效。可选。

存储过程出站网关

以下清单列出了存储过程出站通道适配器的重要属性:

<int-jdbc:stored-proc-outbound-gateway request-channel=""                        // <1>
stored-procedure-name=""
data-source=""
auto-startup="true"
id=""
ignore-column-meta-data="false"
is-function="false"
order=""
reply-channel="" // <2>
reply-timeout="" // <3>
return-value-required="false" // <4>
skip-undeclared-results="" // <5>
sql-parameter-source-factory=""
use-payload-as-parameter-source="">
<int-jdbc:sql-parameter-definition name="" direction="IN"
type=""
scale="10"/>
<int-jdbc:sql-parameter-definition name=""/>
<int-jdbc:parameter name="" type="" value=""/>
<int-jdbc:parameter name="" expression=""/>
<int-jdbc:returning-resultset name="" row-mapper="" />
  • 此端点的接收消息通道。必需。

  • 接收数据库响应后,回复消息应发送到的消息通道。可选。

  • 允许您指定此网关在抛出异常前等待回复消息成功发送的时长。请注意,当发送到 DirectChannel 时,调用发生在发送者的线程中。因此,发送操作的失败可能是由更下游的其他组件引起的。该值以毫秒为单位指定。可选。

  • 指示是否应包含此过程的返回值。可选。

  • 如果 skip-undeclared-results 属性设置为 true,则存储过程调用中所有没有对应 SqlOutParameter 声明的结果都将被忽略。例如,即使您的存储过程只声明了一个结果参数,存储过程也可能返回一个更新计数值。具体行为取决于数据库。该值在底层的 JdbcTemplate 上设置。默认值为 true。可选。

示例

本节包含两个调用 Apache Derby 存储过程的示例。第一个过程调用返回 ResultSet 的存储过程。通过使用 RowMapper,数据被转换为领域对象,随后成为 Spring Integration 消息的有效载荷。

在第二个示例中,我们调用了一个使用输出参数返回数据的存储过程。

备注

请查看 Spring Integration Samples 项目

该项目包含了此处引用的 Apache Derby 示例,以及如何运行它的说明。Spring Integration Samples 项目还提供了一个使用 Oracle 存储过程的示例

在第一个示例中,我们调用了一个名为 FIND_ALL_COFFEE_BEVERAGES 的存储过程,该过程没有定义任何输入参数,但会返回一个 ResultSet

在 Apache Derby 中,存储过程是通过 Java 实现的。以下清单展示了方法签名:

public static void findAllCoffeeBeverages(ResultSet[] coffeeBeverages)
throws SQLException {
...
}

以下清单展示了对应的 SQL 语句:

CREATE PROCEDURE FIND_ALL_COFFEE_BEVERAGES() \
PARAMETER STYLE JAVA LANGUAGE JAVA MODIFIES SQL DATA DYNAMIC RESULT SETS 1 \
EXTERNAL NAME 'o.s.i.jdbc.storedproc.derby.DerbyStoredProcedures.findAllCoffeeBeverages';

在 Spring Integration 中,您现在可以通过例如 stored-proc-outbound-gateway 来调用此存储过程,如下例所示:

<int-jdbc:stored-proc-outbound-gateway id="outbound-gateway-storedproc-find-all"
data-source="dataSource"
request-channel="findAllProcedureRequestChannel"
expect-single-result="true"
stored-procedure-name="FIND_ALL_COFFEE_BEVERAGES">
<int-jdbc:returning-resultset name="coffeeBeverages"
row-mapper="org.springframework.integration.support.CoffeBeverageMapper"/>
</int-jdbc:stored-proc-outbound-gateway>

在第二个示例中,我们调用了一个名为 FIND_COFFEE 的存储过程,它有一个输入参数。该过程不返回 ResultSet,而是使用输出参数。以下示例展示了方法签名:

public static void findCoffee(int coffeeId, String[] coffeeDescription)
throws SQLException {
...
}

下面的清单展示了对应的 SQL:

CREATE PROCEDURE FIND_COFFEE(IN ID INTEGER, OUT COFFEE_DESCRIPTION VARCHAR(200)) \
PARAMETER STYLE JAVA LANGUAGE JAVA EXTERNAL NAME \
'org.springframework.integration.jdbc.storedproc.derby.DerbyStoredProcedures.findCoffee';

在Spring Integration中,您现在可以通过使用例如stored-proc-outbound-gateway来调用此存储过程,如下例所示:

<int-jdbc:stored-proc-outbound-gateway id="outbound-gateway-storedproc-find-coffee"
data-source="dataSource"
request-channel="findCoffeeProcedureRequestChannel"
skip-undeclared-results="true"
stored-procedure-name="FIND_COFFEE"
expect-single-result="true">
<int-jdbc:parameter name="ID" expression="payload" />
</int-jdbc:stored-proc-outbound-gateway>