出站通道适配器

出站通道适配器是入站通道适配器的反面:它的作用是处理消息并使用它来执行 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 键的映射,因此 [] 运算符从映射中取消引用该值。报头也作为映射访问。

前面的查询中的参数是传入消息上的 bean 属性表达式(不是 SpEL 表达式)。此行为是 SqlParameterSource 的一部分,它是出站适配器创建的默认源。您可以注入不同的 SqlParameterSourceFactory 以获得不同的行为。

出站适配器需要引用 DataSourceJdbcTemplate。您还可以注入 SqlParameterSourceFactory 来控制每个传入消息到查询的绑定。

如果输入通道是直接通道,则出站适配器将在与消息发送者相同的线程中运行其查询,因此在与消息发送者相同的交易中运行(如果有)。

使用 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` 的配置,这些消息将用于构建 `SqlParameterSource[]`,作为上述 `JdbcOperations.batchUpdate()` 函数的参数。当应用 `MessagePreparedStatementSetter` 配置时,将使用 `BatchPreparedStatementSetter` 变体来迭代这些消息,并针对每个项目调用提供的 `MessagePreparedStatementSetter`。当选择 `keysGenerated` 模式时,不支持批量更新。