儲存過程
在某些情況下,純粹的 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>