Outbound Channel Adapter

出站通道适配器是入站通道适配器的逆序:它的角色是处理消息并使用它来执行 SQL 查询。默认情况下,消息有效负载和标题可作为查询的输入参数,如下例所示:

The outbound channel adapter is the inverse of the inbound: its role is to handle a message and use it to execute a SQL query. By default, the message payload and headers are available as input parameters to the query, as the following example shows:

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

在前面的示例中,到达标记为 input 的通道的消息有效负载是一个 map,其中一个键为 something,因此 [] 运算符从该 map 对该值进行取消引用。该头信息也被访问为一个 map。

In the preceding example, messages arriving at the channel labelled input have a payload of a map with a key of something, so the [] operator dereferences that value from the map. The headers are also accessed as a map.

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

The parameters in the preceding query are bean property expressions on the incoming message (not SpEL expressions). This behavior is part of the SqlParameterSource, which is the default source created by the outbound adapter. You can inject a different SqlParameterSourceFactory to get different behavior.

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

The outbound adapter requires a reference to either a DataSource or a JdbcTemplate. You can also inject a SqlParameterSourceFactory to control the binding of each incoming message to a query.

如果输入通道是直接通道,那么出站适配器将在同一线程中,因此,与消息发送方处在同一事务(如果存在)中,运行其查询。

If the input channel is a direct channel, the outbound adapter runs its query in the same thread and, therefore, the same transaction (if there is one) as the sender of the message.

Passing Parameters by Using SpEL Expressions

大多数 JDBC 通道适配器的常见要求是将参数作为 SQL 查询或存储过程或函数的一部分传递。如前所述,这些参数默认是 bean 属性表达式,而不是 SpEL 表达式。但是,如果您需要将 SpEL 表达式作为参数传递,则必须显式注入 SqlParameterSourceFactory

A common requirement for most JDBC channel adapters is to pass parameters as part of SQL queries or stored procedures or functions. As mentioned earlier, these parameters are by default bean property expressions, not SpEL expressions. However, if you need to pass SpEL expression as parameters, you must explicitly inject a SqlParameterSourceFactory.

以下示例使用 ExpressionEvaluatingSqlParameterSourceFactory 来满足该要求:

The following example uses a ExpressionEvaluatingSqlParameterSourceFactory to achieve that requirement:

<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>

有关更多信息,请参见 {Defining Parameter Sources}。

For further information, see Defining Parameter Sources.

Using the PreparedStatement Callback

有时,SqlParameterSourceFactory 的灵活性和松耦合并不能满足我们对目标 PreparedStatement 的需求,或者我们需要执行一些低级别的 JDBC 工作。Spring JDBC 模块提供了 API 来配置执行环境(例如 ConnectionCallbackPreparedStatementCreator)并操作参数值(例如 SqlParameterSource)。它甚至可以访问用于低级操作的 API,例如 StatementCallback

Sometimes, the flexibility and loose-coupling of SqlParameterSourceFactory does not do what we need for the target PreparedStatement or we need to do some low-level JDBC work. The Spring JDBC module provides APIs to configure the execution environment (such as ConnectionCallback or PreparedStatementCreator) and manipulate parameter values (such as SqlParameterSource). It can even access APIs for low-level operations, such as StatementCallback.

从 Spring Integration 4.2 开始,MessagePreparedStatementSetter 允许在 requestMessage 上下文中手动指定 PreparedStatement 上的参数。此类在标准 Spring JDBC API 中与 PreparedStatementSetter 扮演完全相同的角色。实际上,它是在 JdbcMessageHandlerJdbcTemplate 调用 execute 时从内联 PreparedStatementSetter 实现中直接调用的。

Starting with Spring Integration 4.2, MessagePreparedStatementSetter allows the specification of parameters on the PreparedStatement manually, in the requestMessage context. This class plays exactly the same role as PreparedStatementSetter in the standard Spring JDBC API. Actually, it is invoked directly from an inline PreparedStatementSetter implementation when the JdbcMessageHandler invokes execute on the JdbcTemplate.

此功能性界面选项与 sqlParameterSourceFactory 互斥,可以用作从 requestMessage 填充 preparedStatement 中的参数的更强大的替代方案。例如,当我们需要以流方式将 File 数据存储到数据库 BLOB 列时,此功能非常有用。以下示例展示了如何执行此操作:

This functional interface option is mutually exclusive with sqlParameterSourceFactory and can be used as a more powerful alternative to populate parameters of the PreparedStatement from the requestMessage. For example, it is useful when we need to store File data to the DataBase BLOB column in a streaming manner. The following example shows how to do so:

@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 配置角度来看,<int-jdbc:outbound-channel-adapter> 组件上可以使用 prepared-statement-setter 属性。它允许你指定 MessagePreparedStatementSetter bean 引用。

From the XML configuration perspective, the prepared-statement-setter attribute is available on the <int-jdbc:outbound-channel-adapter> component. It lets you specify a MessagePreparedStatementSetter bean reference.

Batch Update

从 5.1 版开始,JdbcMessageHandler 如果请求消息的负载是一个 Iterable 实例,则执行 JdbcOperations.batchUpdate()。如果某个元素不是 Message,则会将 Iterable 的每个元素包装到一个 Message 中,其中包含来自请求消息的标头。对于基于常规 SqlParameterSourceFactory 的配置,这些消息用来为上述 JdbcOperations.batchUpdate() 函数中使用的参数构建一个 SqlParameterSource[]。如果应用了 MessagePreparedStatementSetter 配置,则会使用 BatchPreparedStatementSetter 变体为各个项目迭代那些消息,并且会针对这些消息调用提供的 MessagePreparedStatementSetter。如果选择了 keysGenerated 模式,则不支持批更新。

Starting with version 5.1, the JdbcMessageHandler performs a JdbcOperations.batchUpdate() if the payload of the request message is an Iterable instance. Each element of the Iterable is wrapped to a Message with the headers from the request message if such an element is not a Message already. In the case of regular SqlParameterSourceFactory-based configuration these messages are used to build an SqlParameterSource[] for an argument used in the mentioned JdbcOperations.batchUpdate() function. When a MessagePreparedStatementSetter configuration is applied, a BatchPreparedStatementSetter variant is used to iterate over those messages for each item and the provided MessagePreparedStatementSetter is called against them. The batch update is not supported when keysGenerated mode is selected.