存储过程
在某些情况下,仅凭纯 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
即使您的特定数据库可能未完全支持,您仍然很有可能成功使用存储过程 Spring Integration 组件,前提是您的 RDBMS 支持存储过程或存储函数。 事实上,一些提供的集成测试使用了 H2 数据库。 然而,彻底测试这些使用场景非常重要。 |
常用配置属性
所有存储过程组件共享某些配置参数:
-
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
的引用。
默认情况下,传入 Message
消息体的 bean 属性用作存储过程输入参数的来源,方法是使用 BeanPropertySqlParameterSourceFactory
。这对于基本用例可能足够了。
对于更复杂的选项,请考虑传入一个或多个 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 表达式提供。[source, xml]
<int-jdbc:parameter name="" [id="CO1-1"]1 type="" [id="CO1-2"]2 value=""/> [id="CO1-3"]3 <int-jdbc:parameter name="" expression=""/> [id="CO1-4"]4
<1> 要传递给存储过程或存储函数的参数名称。 必需。 <1> 此属性指定值的类型。 如果未提供任何内容,则此属性默认为 `java.lang.String`。 此属性仅在使用 `value` 属性时使用。 可选。 <1> 参数的值。 您必须提供此属性或 `expression` 属性。 可选。 <1> 除了 `value` 属性,您可以指定一个 SpEL 表达式来传递参数的值。 如果您指定 `expression`,则不允许使用 `value` 属性。 可选。 可选。
-
returning-resultset
:存储过程可能返回多个结果集。 通过设置一个或多个returning-resultset
元素,您可以指定RowMappers
将每个返回的ResultSet
转换为有意义的对象。 可选。[source, xml]
<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="" [id="CO2-1"]1 direction="IN" [id="CO2-2"]2 type="STRING" [id="CO2-3"]3 scale="5" [id="CO2-4"]4 type-name="FOO_STRUCT" [id="CO2-5"]5 return-type="fooSqlReturnType"/> [id="CO2-6"]6
<1> 指定 SQL 参数的名称。 必需。 <1> 指定 SQL 参数定义的方向。 默认为 `IN`。 有效值为:`IN`、`OUT` 和 `INOUT`。 如果您的过程返回结果集,请使用 `returning-resultset` 元素。 可选。 <1> 此 SQL 参数定义使用的 SQL 类型。 转换为整数值,如 `java.sql.Types` 所定义。 或者,您也可以提供整数值。 如果未明确设置此属性,则默认为“VARCHAR”。 可选。 <1> SQL 参数的比例。 仅用于数字和小数参数。 可选。 <1> 用户命名类型的 `typeName`,例如:`STRUCT`、`DISTINCT`、`JAVA_OBJECT` 和命名数组类型。 此属性与 `scale` 属性互斥。 可选。 <1> 对复杂类型的自定义值处理程序的引用。 link:https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/jdbc/core/SqlReturnType.html[`SqlReturnType`] 的实现。 此属性与 `scale` 属性互斥,并且仅适用于 OUT 和 INOUT 参数。 可选。 * `poller`:如果此端点是 `PollingConsumer`,则允许您配置消息轮询器。 可选。
定义参数来源
参数来源管理检索 Spring Integration 消息属性并将其映射到相关存储过程输入参数的技术。
存储过程组件遵循某些规则。
默认情况下,Message
消息体的 bean 属性用作存储过程输入参数的来源。
在这种情况下,使用 BeanPropertySqlParameterSourceFactory
。
这对于基本用例可能足够了。
下一个示例说明了该默认行为。
对于使用 BeanPropertySqlParameterSourceFactory
的 bean 属性的“自动
”查找要起作用,您的 bean 属性必须以小写字母定义。
这是因为在 org.springframework.jdbc.core.metadata.CallMetaDataContext
(Java 方法为 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="" [id="CO3-1"]1
stored-procedure-name=""
data-source=""
auto-startup="true"
id=""
ignore-column-meta-data="false"
is-function="false"
skip-undeclared-results="" [id="CO3-2"]2
return-value-required="false" [id="CO3-3"]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>
<1> 轮询消息发送到的通道。 如果存储过程或函数不返回任何数据,则 `Message` 的消息体为 null。 必需。 <1> 如果此属性设置为 `true`,则跳过所有没有相应 `SqlOutParameter` 声明的存储过程调用结果。 例如,存储过程可以返回更新计数,即使您的存储过程只声明了一个结果参数。 确切的行为取决于数据库实现。 该值设置在底层的 `JdbcTemplate` 上。 该值默认为 `true`。 可选。 <1> 指示是否应包含此过程的返回值。 自 Spring Integration 3.0 起。 可选。
存储过程出站通道适配器
以下列表列出了对存储过程出站通道适配器重要的属性:
<int-jdbc:stored-proc-outbound-channel-adapter channel="" [id="CO4-1"]1
stored-procedure-name=""
data-source=""
auto-startup="true"
id=""
ignore-column-meta-data="false"
order="" [id="CO4-2"]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>
<1> 此端点的接收消息通道。 必需。 <1> 当此端点作为订阅者连接到通道时,指定调用的顺序。 当该通道使用 `failover` 调度策略时,这尤其相关。 当此端点本身是带有队列的通道的轮询消费者时,它不起作用。 可选。
存储过程出站网关
以下列表列出了对存储过程出站通道适配器重要的属性:
<int-jdbc:stored-proc-outbound-gateway request-channel="" [id="CO5-1"]1
stored-procedure-name=""
data-source=""
auto-startup="true"
id=""
ignore-column-meta-data="false"
is-function="false"
order=""
reply-channel="" [id="CO5-2"]2
reply-timeout="" [id="CO5-3"]3
return-value-required="false" [id="CO5-4"]4
skip-undeclared-results="" [id="CO5-5"]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="" />
<1> 此端点的接收消息通道。 必需。 <1> 收到数据库响应后,回复消息应发送到的消息通道。 可选。 <1> 允许您指定此网关等待回复消息成功发送的时间,超过此时间将抛出异常。 请记住,当发送到 `DirectChannel` 时,调用发生在发送者的线程中。 因此,发送操作的失败可能是由更下游的其他组件引起的。 该值以毫秒为单位指定。 可选。 <1> 指示是否应包含此过程的返回值。 可选。 <1> 如果 `skip-undeclared-results` 属性设置为 `true`,则跳过所有没有相应 `SqlOutParameter` 声明的存储过程调用结果。 例如,存储过程可以返回更新计数,即使您的存储过程只声明了一个结果参数。 确切的行为取决于数据库。 该值设置在底层的 `JdbcTemplate` 上。 该值默认为 `true`。 可选。
示例
本节包含两个调用 Apache Derby 存储过程的示例。
第一个过程调用返回 ResultSet
的存储过程。
通过使用 RowMapper
,数据被转换为域对象,然后成为 Spring Integration 消息体。
在第二个示例中,我们调用使用输出参数返回数据的存储过程。
请查看 Spring Integration 示例项目。 该项目包含此处引用的 Apache Derby 示例,以及如何运行它的说明。 Spring Integration 示例项目还提供了使用 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>