|
如需使用最新稳定版本,请使用 Spring Integration 7.0.4! |
存储过程
在某些情况下,原生的 JDBC 支持可能不足以满足需求。 您可能需要处理遗留的关系型数据库架构,或者具有复杂的数据处理需求,但最终必须使用 存储过程 或存储函数。 自 Spring Integration 2.1 版本起,我们提供了三个组件来执行存储过程或存储函数:
-
存储过程入站通道适配器
-
存储过程出站通道适配器
-
存储过程出站网关
支持的数据库
为了启用对存储过程和存储函数的调用,存储过程组件使用 org.springframework.jdbc.core.simple.SimpleJdbcCall 类。
因此,以下数据库完全支持执行存储过程:
-
Apache Derby
-
DB2
-
MySQL
-
Microsoft SQL Server
-
Oracle
-
PostgreSQL
-
赛伯斯
如果您想执行存储函数,以下数据库完全支持:
-
MySQL
-
Microsoft SQL Server
-
Oracle
-
PostgreSQL
|
即使您特定的数据库可能未得到完全支持,只要您的 RDBMS 支持存储过程或存储函数,您仍然很有可能成功使用 Spring Integration 的存储过程组件。 事实上,一些提供的集成测试使用了 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 导出器。 -
sql-parameter-source-factory: (存储过程入站适配器不可用。) 对SqlParameterSourceFactory的引用。 默认情况下,传入的Message负载的 Bean 属性被用作存储过程输入参数的来源,方法是使用BeanPropertySqlParameterSourceFactory。这足以满足基本用例。 对于更复杂的选项,请考虑传入一个或多个
ProcedureParameter值。 请参阅 定义参数源。 可选。 -
use-payload-as-parameter-source:(存储过程入站通道适配器不可用。) 如果设置为true,则使用Message的负载作为提供参数的来源。 但如果设置为false,则整个Message都可用作参数的来源。如果没有传入过程参数,此属性默认为
true。 这意味着,通过使用默认值BeanPropertySqlParameterSourceFactory,负载的 bean 属性将用作存储过程或存储函数的参数值的来源。然而,如果传递了过程参数,此属性(默认情况下)求值为
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)1 要传递给存储过程或存储函数的参数名称。 必需。 2 此属性指定值的类型。 如果未提供此属性,则默认为 java.lang.String。 仅在使用value属性时才使用此属性。 可选。3 参数的值。 您必须提供此属性或 expression属性。 可选。4 您可以使用 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)
| 1 | 指定 SQL 参数的名称。 必填项。 |
| 2 | 指定 SQL 参数定义的方向。
默认为 IN。
有效值为:IN、OUT 和 INOUT。
如果您的存储过程返回结果集,请使用 returning-resultset 元素。
可选。 |
| 3 | 此 SQL 参数定义所使用的 SQL 类型。
转换为整数类型,如 java.sql.Types 所定义。
您也可以选择提供整数值。
如果未显式设置此属性,则默认为 'VARCHAR'。
可选。 |
| 4 | SQL 参数的规模。 仅用于数值和十进制参数。 可选。 |
| 5 | 对于用户命名的类型(例如:STRUCT, DISTINCT, JAVA_OBJECT)以及命名数组类型,typeName 表示该情况。
此属性与 scale 属性互斥。
可选。 |
| 6 | 对复杂类型的自定义值处理程序的引用。
SqlReturnType 的实现。
此属性与 scale 属性互斥,仅适用于 OUT 和 INOUT 参数。
可选。
|
定义参数源
参数源控制检索和映射 Spring Integration 消息属性到相关存储过程输入参数的技术。
存储过程组件遵循特定规则。
默认情况下,Message负载的bean属性被用作存储过程输入参数的来源。
在这种情况下,将使用BeanPropertySqlParameterSourceFactory。
这可能足以满足基本用例。
下一个示例说明了这种默认行为。
为了使使用 BeanPropertySqlParameterSourceFactory 进行 bean 属性的“自动”查找正常工作,您的 bean 属性必须以小写字母定义。
这是因为在 org.springframework.jdbc.core.metadata.CallMetaDataContext(Java 方法为 matchInParameterValuesWithCallParameters())中,检索到的存储过程参数声明会被转换为小写。
因此,如果您使用了驼峰命名法的 bean 属性(例如 lastName),查找将会失败。
在这种情况下,请提供一个显式的 ProcedureParameter。 |
假设我们有一个负载,它由一个简单的 Bean 组成,该 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>
| 1 | 接收轮询消息的通道。
如果存储过程或函数不返回任何数据,则 Message 的有效负载为 null。
必填项。 |
| 2 | 如果此属性设置为 true,则存储过程调用中所有没有对应 SqlOutParameter 声明的结果都将被跳过。
例如,即使您的存储过程仅声明了一个结果参数,存储过程仍可能返回更新计数值。
具体行为取决于数据库实现。
该值设置在底层 JdbcTemplate 上。
该值的默认值为 true。
可选。 |
| 3 | 指示是否应包含此过程的返回值。 自 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>
| 1 | 此端点的接收消息通道。 必需。 |
| 2 | 指定当此端点作为订阅者连接到通道时的调用顺序。
这在通道使用 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="" />
| 1 | 此端点的接收消息通道。 必需。 |
| 2 | 接收数据库响应后应发送回复的消息通道。 可选。 |
| 3 | 允许您指定此网关在抛出异常之前等待回复消息成功发送的时长。
请注意,当向 DirectChannel 发送时,调用将在发送者的线程中发生。
因此,发送操作的失败可能是由下游的其他组件引起的。
该值以毫秒为单位指定。
可选。 |
| 4 | 指示是否应包含此过程的返回值。 可选。 |
| 5 | 如果 skip-undeclared-results 属性设置为 true,则存储过程调用中所有没有对应 SqlOutParameter 声明的结果都将被跳过。
例如,存储过程可能会返回更新计数值,即使您的存储过程仅声明了一个结果参数。
确切的行为取决于数据库。
该值在底层 JdbcTemplate 上设置。
该值的默认值为 true。
可选。 |
示例
本部分包含两个调用 Apache Derby 存储过程的示例。
第一个过程调用一个返回 ResultSet 的存储过程。
通过使用 RowMapper,数据被转换为领域对象,随后该对象成为 Spring Integration 消息的有效负载。
在第二个示例中,我们调用一个存储过程,该过程使用输出参数来返回数据。
|
该项目包含此处引用的 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>