我正在构建一个应用程序(使用spring-integration),它应该从whatever-inbound-gateway接收Request对象并调用MS SQL存储过程。
Request对象包含String
属性procedureName
和List<Pair<String, Object>>
属性parameters
。问题是我无法找到解决方案,当我在编译时不知道它的长度时,如何传递参数列表。
配置出站网关:
<int-jdbc:stored-proc-outbound-gateway
id="outbound-gateway-procedure" request-channel="requestChannel"
data-source="dataSource" stored-procedure-name-expression="payload.procedureName"
sql-parameter-source-factory="someParameterSourceFactory" >
</int-jdbc:stored-proc-outbound-gateway>
如果我知道列表中只有2个参数,而且他们的名字是“第一个&#39;和&#39;秒&#39;,我会像这样配置sql-parameter-source-factory
:
<bean id="someParameterSourceFactory" class="org.springframework.integration.jdbc.ExpressionEvaluatingSqlParameterSourceFactory">
<property name="parameterExpressions">
<map>
<entry key="first" value-type="java.lang.String" value="payload.parameters.get(1).second"/>
<entry key="second" value-type="java.lang.String" value="payload.parameters.get(2).second"/>
</map>
</property>
</bean>
但是我不知道在编译时参数的数量都没有。有什么建议吗?
UPD: 存储过程如下所示:
create procedure [dbo].[pnzLimitUtilization_Show]
(
@BookDate datetime
, @LegalGroupList varchar(4000)
, @Currency char(3)
, @FilterForAny nvarchar(255) = ''
, @RiskZone char(1) = 'A'
, @WithZerroExposure char(1) = '0'
, @Transliteration bit = 0
, @IsNewMethodology bit = 0
, @UtilizationType char(1) = 'A'
, @PartyGroupList varchar(255) = NULL
, @HostName varchar(255) = NULL
, @ShowCloseExpiration bit = 0
, @CloseExpirationDays int = 90
, @WhatIfDealFilter char(1) = 'R'
, @HideLESublimits bit = 0
)
as begin ....
新配置:
<int-jdbc:stored-proc-outbound-gateway
id="outbound-gateway-procedure" request-channel="requestChannel"
data-source="dataSource" stored-procedure-name-expression="payload.name.toString()"
ignore-column-meta-data="true"
sql-parameter-source-factory="listParameterSourceFactory"
reply-channel="replyChannel">
</int-jdbc:stored-proc-outbound-gateway>
<bean id="listParameterSourceFactory" class="my.pack.integration.ListSqlParameterSourceFactory" />
ListSqlParameterSourceFactory:
public class ListSqlParameterSourceFactory implements SqlParameterSourceFactory {
@Override
@SuppressWarnings("unchecked")
public SqlParameterSource createParameterSource(Object input) {
GetSourceDataRequest message = (GetSourceDataRequest) input;
List<NamedValueEntity> params = message.getParameters();
MapSqlParameterSource source = new MapSqlParameterSource();
for (NamedValueEntity param : params) {
source.addValue(param.getName().toString(), param.getValue());
}
return source;
}
}
现在我收到错误
Caused by: org.springframework.jdbc.BadSqlGrammarException: CallableStatementCallback; bad SQL grammar [{call dbo.pnzLimitUtilization_Show()}]; nested exception is java.sql.SQLException: Procedure or function 'pnzLimitUtilization_Show' expects parameter '@BookDate', which was not supplied.
at org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:97)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
我通过ListSqlParameterSourceFactory进行了调试,它按预期工作,SqlParameterSource是
[0] = {java.util.HashMap$Entry@4243}"@Currency" -> "R"
[1] = {java.util.HashMap$Entry@4246}"@LegalGroupList" -> "( ALL )"
[2] = {java.util.HashMap$Entry@4249}"@FilterForAny" -> ""
[3] = {java.util.HashMap$Entry@4252}"@ShowCloseExpiration" -> "0"
[4] = {java.util.HashMap$Entry@4255}"@IsNewMethodology" -> "0"
[5] = {java.util.HashMap$Entry@4258}"@CloseExpirationDays" -> "0"
[6] = {java.util.HashMap$Entry@4261}"@WhatIfDealFilter" -> "R"
[7] = {java.util.HashMap$Entry@4264}"@UtilizationType" -> "A"
[8] = {java.util.HashMap$Entry@4267}"@BookDate" -> "20141202 00:00:00.000"
[9] = {java.util.HashMap$Entry@4270}"@Transliteration" -> "0"
[10] = {java.util.HashMap$Entry@4273}"@PartyGroupList" -> "( ALL )"
[11] = {java.util.HashMap$Entry@4276}"@RiskZone" -> "L"
[12] = {java.util.HashMap$Entry@4279}"@HideLESublimits" -> "0"
我错过了什么吗?
UPD2: Spring-jdbc日志:
05 dec 2014 14:15:28 DEBUG SimpleJdbcCall - Compiled stored procedure. Call string is [{call dbo.pnzLimitUtilization_Show()}]
05 dec 2014 14:15:28 DEBUG SimpleJdbcCall - SqlCall for procedure [dbo.pnzLimitUtilization_Show] compiled
05 dec 2014 14:15:28 DEBUG CallMetaDataContext - Matching [@FilterForAny, @BookDate, @Currency, @UtilizationType, @HideLESublimits, @ShowCloseExpiration, @Transliteration, @IsNewMethodology, @RiskZone, @WhatIfDealFilter, @CloseExpirationDays, @PartyGroupList, @LegalGroupList] with []
05 dec 2014 14:15:28 DEBUG CallMetaDataContext - Found match for []
05 dec 2014 14:15:28 DEBUG SimpleJdbcCall - The following parameters are used for call {call dbo.pnzLimitUtilization_Show()} with: {}
05 dec 2014 14:15:28 DEBUG JdbcTemplate - Calling stored procedure [{call dbo.pnzLimitUtilization_Show()}]
看起来它没有传递任何参数..
调试CallMetaDataContext我发现this.callParameters
为空。它与ignore-column-meta-data="true"
没有联系吗?但如果我将其切换到false
,我会看到另一个错误:
05 dec 2014 14:41:33 DEBUG CallMetaDataProviderFactory - Using org.springframework.jdbc.core.metadata.SqlServerCallMetaDataProvider
05 dec 2014 14:41:33 DEBUG CallMetaDataProvider - Retrieving metadata for null/null/dbo.pnzLimitUtilization_Show
05 dec 2014 14:41:36 DEBUG DataSourceUtils - Returning JDBC Connection to DataSource
org.springframework.messaging.MessageHandlingException: error occurred in message handler [org.springframework.integration.jdbc.StoredProcOutboundGateway#0]; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: Unable to determine the correct call signature for dbo.pnzLimitUtilization_Show - package name should be specified separately using '.withCatalogName("dbo")'
UPD3:
好的,我从过程名称中删除了dbo.
,从参数名称中删除了@
,现在它正确地加载了列元数据并绑定了变量。这并不意味着我的应用程序有效,但至少我可以更进一步。 Artem的建议有助于解决主要问题。
答案 0 :(得分:1)
首先,您的PROCEDURE看起来如何?至少是规范。
为什么您确定任何参数列表对于您的过程调用是合法的?
从另一方面来说,您可以使用SqlParameterSourceFactory
自定义,但它确实应该完全自定义实现。这样的话,因为你说你的payload
有parameters
属性:
public class PairSqlParameterSourceFactory implements SqlParameterSourceFactory {
public SqlParameterSource createParameterSource(Object input) {
Message<Request> message = (Message<Request>) input;
List<Pair<String, Object>> pairs = message.getPayload().getParameters();
MapSqlParameterSource source = new MapSqlParameterSource();
for (Pair<String, Object> pair : pairs) {
source.addValue(pair.getName(), pair.getValue());
}
return source;
}
}