入站通道介面卡

入站通道介面卡的主要功能是執行 SQL SELECT 查詢並將結果集轉換為訊息。訊息負載是整個結果集(表示為一個 List),列表項的型別取決於行對映策略。預設策略是一個通用對映器,它為查詢結果中的每一行返回一個 Map。您可以選擇透過新增對 RowMapper 例項的引用來更改此行為(有關行對映的詳細資訊,請參閱Spring JDBC 文件)。

如果您希望將 SELECT 查詢結果中的行轉換為單獨的訊息,可以使用下游的拆分器(splitter)。

入站介面卡還需要對 JdbcTemplate 例項或 DataSource 的引用。

除了用於生成訊息的 SELECT 語句外,介面卡還有一個 UPDATE 語句,用於將記錄標記為已處理,以便它們不會出現在下一次輪詢中。更新操作可以透過原始 SELECT 查詢返回的 ID 列表進行引數化。預設情況下,這是透過命名約定完成的(輸入結果集中名為 id 的列會被轉換為更新操作的引數 Map 中名為 id 的列表)。以下示例定義了一個帶有更新查詢和 DataSource 引用的入站通道介面卡。

<int-jdbc:inbound-channel-adapter query="select * from item where status=2"
    channel="target" data-source="dataSource"
    update="update item set status=10 where id in (:id)" />
更新查詢中的引數使用引數名稱前加冒號(:)指定(在前一個示例中,這是一個應用於輪詢結果集中每行的表示式)。這是 Spring JDBC 中命名引數 JDBC 支援的標準特性,並結合了 Spring Integration 中採用的約定(投影到輪詢結果列表)。底層的 Spring JDBC 功能限制了可用表示式(例如,大多數特殊字元除了句點外都不允許),但由於目標通常是一個物件列表(可能是單項列表),這些物件可以透過 bean 路徑定址,因此這並沒有過度限制。

要更改引數生成策略,您可以向介面卡注入 SqlParameterSourceFactory 來覆蓋預設行為(介面卡有一個 sql-parameter-source-factory 屬性)。Spring Integration 提供了 ExpressionEvaluatingSqlParameterSourceFactory,它建立一個基於 SpEL 的引數源,並將查詢結果作為 #root 物件。(如果 update-per-row 為 true,則根物件是單行)。如果同一個引數名稱在更新查詢中多次出現,它只會被求值一次,並且其結果會被快取。

您還可以為 select 查詢使用引數源。在這種情況下,由於沒有要對其進行求值的“結果”物件,因此每次使用單個引數源(而不是使用引數源工廠)。從 4.0 版本開始,您可以使用 Spring 建立基於 SpEL 的引數源,如下例所示

<int-jdbc:inbound-channel-adapter query="select * from item where status=:status"
	channel="target" data-source="dataSource"
	select-sql-parameter-source="parameterSource" />

<bean id="parameterSource" factory-bean="parameterSourceFactory"
			factory-method="createParameterSourceNoCache">
	<constructor-arg value="" />
</bean>

<bean id="parameterSourceFactory"
		class="o.s.integration.jdbc.ExpressionEvaluatingSqlParameterSourceFactory">
	<property name="parameterExpressions">
		<map>
			<entry key="status" value="@statusBean.which()" />
		</map>
	</property>
</bean>

<bean id="statusBean" class="foo.StatusDetermination" />

每個引數表示式中的 value 可以是任何有效的 SpEL 表示式。表示式求值的 #root 物件是在 parameterSource bean 上定義的建構函式引數。對於所有求值,它都是靜態的(在前一個示例中,是一個空的 String)。

從 5.0 版本開始,您可以為 ExpressionEvaluatingSqlParameterSourceFactory 提供 sqlParameterTypes 以指定特定引數的目標 SQL 型別。

以下示例為查詢中使用的引數提供了 SQL 型別

<int-jdbc:inbound-channel-adapter query="select * from item where status=:status"
    channel="target" data-source="dataSource"
    select-sql-parameter-source="parameterSource" />

<bean id="parameterSource" factory-bean="parameterSourceFactory"
            factory-method="createParameterSourceNoCache">
    <constructor-arg value="" />
</bean>

<bean id="parameterSourceFactory"
        class="o.s.integration.jdbc.ExpressionEvaluatingSqlParameterSourceFactory">
    <property name="sqlParameterTypes">
        <map>
            <entry key="status" value="#{ T(java.sql.Types).BINARY}" />
        </map>
    </property>
</bean>
使用 createParameterSourceNoCache 工廠方法。否則,引數源會快取求值結果。另請注意,由於快取被停用,如果同一個引數名稱在 select 查詢中多次出現,則每次出現時都會重新求值。

輪詢與事務

入站介面卡接受常規的 Spring Integration 輪詢器作為子元素。因此,可以控制輪詢的頻率(以及其他用途)。對於 JDBC 用法,輪詢器的一個重要特性是可以在事務中包裝輪詢操作,如下例所示

<int-jdbc:inbound-channel-adapter query="..."
        channel="target" data-source="dataSource" update="...">
    <int:poller fixed-rate="1000">
        <int:transactional/>
    </int:poller>
</int-jdbc:inbound-channel-adapter>
如果您沒有顯式指定輪詢器,則使用預設值。與 Spring Integration 通常的做法一樣,預設輪詢器可以定義為頂級 Bean)。

在前面的示例中,資料庫每 1000 毫秒(即每秒一次)輪詢一次,並且更新和 SELECT 查詢都在同一事務中執行。此處未顯示事務管理器配置。但是,只要事務管理器知道資料來源,輪詢就是事務性的。一個常見的用例是下游通道是直接通道(預設),這樣端點在同一執行緒中呼叫,因此也在同一事務中。這樣,如果其中任何一個失敗,事務將回滾,輸入資料將恢復到其原始狀態。

max-rowsmax-messages-per-poll

JDBC 入站通道介面卡定義了一個名為 max-rows 的屬性。當您指定介面卡的輪詢器時,您還可以定義一個名為 max-messages-per-poll 的屬性。雖然這兩個屬性看起來相似,但它們的含義卻完全不同。

max-messages-per-poll 指定每個輪詢間隔內查詢執行的次數,而 max-rows 指定每次執行返回的行數。

在正常情況下,您可能不希望在使用 JDBC 入站通道介面卡時設定輪詢器的 max-messages-per-poll 屬性。其預設值為 1,這意味著 JDBC 入站通道介面卡的 receive() 方法在每個輪詢間隔內精確執行一次。

max-messages-per-poll 屬性設定為更大的值意味著查詢會連續執行多次。有關 max-messages-per-poll 屬性的更多資訊,請參閱配置入站通道介面卡

相比之下,max-rows 屬性(如果大於 0)指定了從 receive() 方法建立的查詢結果集中使用的最大行數。如果屬性設定為 0,則所有行都包含在結果訊息中。此屬性預設為 0

建議透過特定供應商的查詢選項來限制結果集,例如 MySQL 的 LIMIT 或 SQL Server 的 TOP 或 Oracle 的 ROWNUM。有關詳細資訊,請參閱特定供應商的文件。