跳到主要内容
版本: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 定义,该定义是 EventDrivenConsumer 或 PollingConsumer 的实例,具体取决于出站通道适配器的 channel 属性引用的是 SubscribableChannel 还是 PollableChannel。可选。

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

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

  • is-function:如果为 true,则调用 SQL 函数。在这种情况下,stored-procedure-name 或 stored-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,则作为键。

    但是,如果传入了过程参数,则此属性(默认情况下)评估为 false。ProcedureParameter 允许提供 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。有效值为:IN、OUT 和 INOUT。如果过程返回结果集,请使用 returning-resultset 元素。可选。

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

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

  • 用户命名类型的 typeName,例如:STRUCT、DISTINCT、JAVA_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作为有效载荷:id、name 和 description。此外,我们有一个名为 INSERT_COFFEE 的简单存储过程,它接受三个输入参数:id、name 和 description。同时,我们使用一个完全支持的数据库。在这种情况下,以下存储过程出站适配器的配置就足够了:

<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>