預存程序

在某些情況下,純 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 資料庫。儘管如此,徹底測試這些使用案例非常重要。

設定

預存程序元件提供完整的 XML 命名空間支援,並且元件的設定與先前討論的通用 JDBC 元件類似。

通用設定屬性

所有預存程序元件都共用某些設定參數

  • auto-startup:生命週期屬性,表示此元件是否應在應用程式內容啟動期間啟動。預設值為 true。選用。

  • data-source:對 javax.sql.DataSource 的參考,用於存取資料庫。必要。

  • id:識別基礎 Spring bean 定義,它是 EventDrivenConsumerPollingConsumer 的實例,具體取決於輸出通道适配器的 channel 屬性是否參考 SubscribableChannelPollableChannel。選用。

  • ignore-column-meta-data:對於完全支援的資料庫,基礎SimpleJdbcCall 類別可以從 JDBC 中繼資料自動擷取預存程序或預存函數的參數資訊。

    但是,如果資料庫不支援中繼資料查詢,或者您需要提供自訂參數定義,則可以將此旗標設定為 true。預設值為 false。選用。

  • is-function:如果為 true,則會呼叫 SQL 函數。在這種情況下,stored-procedure-namestored-procedure-name-expression 屬性會定義所呼叫函數的名稱。預設值為 false。選用。

  • stored-procedure-name:此屬性指定預存程序的名稱。如果 is-function 屬性設定為 true,則此屬性會改為指定函數名稱。必須指定此屬性或 stored-procedure-name-expression

  • stored-procedure-name-expression:此屬性透過使用 SpEL 運算式來指定預存程序的名稱。透過使用 SpEL,您可以存取完整的訊息(如果可用),包括其標頭和 Payload。您可以使用此屬性在執行階段調用不同的預存程序。例如,您可以提供您想要作為訊息標頭執行的預存程序名稱。運算式必須解析為 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 Payload 的 bean 屬性會用作預存程序輸入參數的來源,方法是使用 BeanPropertySqlParameterSourceFactory

    這可能足以應付基本使用案例。對於更複雜的選項,請考慮傳入一或多個 ProcedureParameter 值。請參閱定義參數來源。選用。

  • use-payload-as-parameter-source:(不適用於預存程序輸入通道适配器。)如果設定為 true,則 Message 的 Payload 會用作提供參數的來源。但是,如果設定為 false,則整個 Message 都可用作參數的來源。

    如果未傳入任何程序參數,則此屬性預設為 true。這表示,透過使用預設 BeanPropertySqlParameterSourceFactory,Payload 的 bean 屬性會用作預存程序或預存函數的參數值的來源。

    但是,如果傳入程序參數,則此屬性(依預設)會評估為 falseProcedureParameter 允許提供 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。有效值為:INOUTINOUT。如果您的程序傳回結果集,請使用 returning-resultset 元素。選用。
3 用於此 SQL 參數定義的 SQL 類型。轉換為整數值,如 java.sql.Types 所定義。或者,您也可以提供整數值。如果未明確設定此屬性,則預設為 'VARCHAR'。選用。
4 SQL 參數的縮放比例。僅用於數值和十進位參數。選用。
5 使用者命名類型的 typeName,例如:STRUCTDISTINCTJAVA_OBJECT 和具名陣列類型。此屬性與 scale 屬性互斥。選用。
6 對複雜類型的自訂值處理常式的參考。SqlReturnType 的實作。此屬性與 scale 屬性互斥,且僅適用於 OUT 和 INOUT 參數。選用。
  • poller:如果此端點是 PollingConsumer,則可讓您設定訊息輪詢器。選用。

定義參數來源

參數來源控管擷取和對應 Spring Integration 訊息屬性到相關預存程序輸入參數的技術。

預存程序元件遵循某些規則。依預設,Message Payload 的 bean 屬性會用作預存程序輸入參數的來源。在這種情況下,會使用 BeanPropertySqlParameterSourceFactory。這可能足以應付基本使用案例。下一個範例說明了該預設行為。

為了讓使用 BeanPropertySqlParameterSourceFactory 自動查閱 bean 屬性能夠運作,您的 bean 屬性必須以小寫定義。這是因為在 org.springframework.jdbc.core.metadata.CallMetaDataContext 中(Java 方法為 matchInParameterValuesWithCallParameters()),擷取的預存程序參數宣告會轉換為小寫。因此,如果您有駝峰式 bean 屬性(例如 lastName),則查閱會失敗。在這種情況下,請提供明確的 ProcedureParameter

假設我們有一個 Payload,其中包含一個簡單的 bean,具有以下三個屬性:idnamedescription。此外,我們有一個簡單的預存程序,名為 INSERT_COFFEE,它接受三個輸入參數:idnamedescription。我們也使用完全支援的資料庫。在這種情況下,預存程序輸出适配器的以下設定就足夠了

<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 的 Payload 為 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 訊息 Payload。

在第二個範例中,我們呼叫使用輸出參數傳回資料的預存程序。

該專案包含此處參考的 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>