存储过程
在某些情况下,纯粹的 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 database。尽管如此,彻底测试这些使用场景非常重要。 |
通用配置属性
所有存储过程组件共享一些配置参数
-
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 属性将用作存储过程或存储函数的参数值的来源。但是,如果传入了过程参数,此属性(默认情况下)评估为
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 除了 value
属性,你还可以指定一个 SpEL 表达式来传递参数的值。如果你指定了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 | 用户命名类型的 typeName ,例如:STRUCT 、DISTINCT 、JAVA_OBJECT 和命名数组类型。此属性与 scale 属性互斥。可选。 |
6 | 对复杂类型自定义值处理器的引用。一个 SqlReturnType 的实现。此属性与 scale 属性互斥,并且仅适用于 OUT 和 INOUT 参数。可选。
|
定义参数源
参数源控制着检索 Spring Integration 消息属性并将其映射到相关存储过程输入参数的技术。
存储过程组件遵循一定的规则。默认情况下,Message
有效载荷的 bean 属性将作为存储过程输入参数的来源。在这种情况下,会使用 BeanPropertySqlParameterSourceFactory
。这对于基本用例可能已经足够。下一个示例演示了此默认行为。
为了让使用 BeanPropertySqlParameterSourceFactory 进行 bean 属性的“自动”查找工作,你的 bean 属性必须以小写形式定义。这是因为在 org.springframework.jdbc.core.metadata.CallMetaDataContext 中(Java 方法是 matchInParameterValuesWithCallParameters() ),检索到的存储过程参数声明会被转换为小写。因此,如果你有驼峰式命名法(例如 lastName )的 bean 属性,查找会失败。在这种情况下,请提供显式的 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>
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>