出站通道介面卡

出站通道介面卡與入站通道介面卡相反:它的作用是處理訊息並用它來執行 SQL 查詢。預設情況下,訊息體和訊息頭可用作查詢的輸入引數,如下例所示

<int-jdbc:outbound-channel-adapter
    query="insert into items (id, status, name) values (:headers[id], 0, :payload[something])"
    data-source="dataSource"
    channel="input"/>

在前面的示例中,到達標籤為 input 的通道的訊息的訊息體是一個包含鍵 something 的 Map,因此 [] 運算子用於從 Map 中解引用該值。訊息頭也可以作為 Map 訪問。

前面查詢中的引數是傳入訊息上的 bean 屬性表示式(不是 SpEL 表示式)。這種行為是 SqlParameterSource 的一部分,它是出站介面卡建立的預設來源。你可以注入不同的 SqlParameterSourceFactory 來獲取不同的行為。

出站介面卡需要引用 DataSourceJdbcTemplate。你也可以注入 SqlParameterSourceFactory 來控制每條傳入訊息與查詢的繫結。

如果輸入通道是 Direct Channel,則出站介面卡將在與訊息傳送者相同的執行緒中執行其查詢,因此也在相同的事務中(如果存在)。

使用 SpEL 表示式傳遞引數

大多數 JDBC 通道介面卡的常見要求是作為 SQL 查詢或儲存過程或函式的一部分傳遞引數。如前所述,這些引數預設是 bean 屬性表示式,而不是 SpEL 表示式。但是,如果你需要將 SpEL 表示式作為引數傳遞,則必須顯式注入 SqlParameterSourceFactory

以下示例使用 ExpressionEvaluatingSqlParameterSourceFactory 來實現該要求

<jdbc:outbound-channel-adapter data-source="dataSource" channel="input"
    query="insert into MESSAGES (MESSAGE_ID,PAYLOAD,CREATED_DATE) values (:id, :payload, :createdDate)"
    sql-parameter-source-factory="spelSource"/>

<bean id="spelSource"
      class="o.s.integration.jdbc.ExpressionEvaluatingSqlParameterSourceFactory">
    <property name="parameterExpressions">
        <map>
            <entry key="id"          value="headers['id'].toString()"/>
            <entry key="createdDate" value="new java.util.Date()"/>
            <entry key="payload"     value="payload"/>
        </map>
    </property>
</bean>

更多資訊,請參閱定義引數來源

使用 PreparedStatement 回撥

有時,SqlParameterSourceFactory 的靈活性和松耦合性無法滿足目標 PreparedStatement 的需求,或者我們需要進行一些低階 JDBC 操作。Spring JDBC 模組提供了 API 來配置執行環境(例如 ConnectionCallbackPreparedStatementCreator)和操作引數值(例如 SqlParameterSource)。它甚至可以訪問低階操作的 API,例如 StatementCallback

從 Spring Integration 4.2 開始,MessagePreparedStatementSetter 允許在 requestMessage 上下文中手動指定 PreparedStatement 的引數。此類的作用與標準 Spring JDBC API 中的 PreparedStatementSetter 完全相同。實際上,當 JdbcMessageHandlerJdbcTemplate 上呼叫 execute 時,它會直接從內聯 PreparedStatementSetter 實現中呼叫。

此函式式介面選項與 sqlParameterSourceFactory 互斥,並且可以作為從 requestMessage 填充 PreparedStatement 引數的更強大替代方案。例如,當我們以流式方式將 File 資料儲存到資料庫的 BLOB 列時,它非常有用。以下示例展示瞭如何實現:

@Bean
@ServiceActivator(inputChannel = "storeFileChannel")
public MessageHandler jdbcMessageHandler(DataSource dataSource) {
    JdbcMessageHandler jdbcMessageHandler = new JdbcMessageHandler(dataSource,
            "INSERT INTO imagedb (image_name, content, description) VALUES (?, ?, ?)");
    jdbcMessageHandler.setPreparedStatementSetter((ps, m) -> {
        ps.setString(1, m.getHeaders().get(FileHeaders.FILENAME));
        try (FileInputStream inputStream = new FileInputStream((File) m.getPayload()); ) {
            ps.setBlob(2, inputStream);
        }
        catch (Exception e) {
            throw new MessageHandlingException(m, e);
        }
        ps.setClob(3, new StringReader(m.getHeaders().get("description", String.class)));
    });
    return jdbcMessageHandler;
}

從 XML 配置的角度來看,prepared-statement-setter 屬性可在 <int-jdbc:outbound-channel-adapter> 元件上使用。它允許你指定 MessagePreparedStatementSetter bean 引用。

批次更新

從 5.1 版本開始,如果請求訊息的訊息體是 Iterable 例項,JdbcMessageHandler 將執行 JdbcOperations.batchUpdate()。如果 Iterable 的每個元素本身不是 Message,則將其包裝成一個帶有請求訊息頭的 Message。在常規的基於 SqlParameterSourceFactory 的配置情況下,這些訊息被用於構建用於上述 JdbcOperations.batchUpdate() 函式的引數 SqlParameterSource[]。當應用 MessagePreparedStatementSetter 配置時,將使用 BatchPreparedStatementSetter 變體來迭代這些訊息中的每個專案,並針對它們呼叫提供的 MessagePreparedStatementSetter。當選擇 keysGenerated 模式時,不支援批次更新。