跳到主要内容
版本:7.0.2

出站通道适配器

DeepSeek V3 中英对照 Outbound Channel Adapter

出站通道适配器是入站通道适配器的反向操作:其职责是处理消息并利用它来执行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 来控制每个传入消息到查询的绑定。为了更顺畅地使用 SqlParameterSourceFactory(特别是默认的 BeanPropertySqlParameterSourceFactory 及其 MapSqlParameterSource),从版本 6.5 开始,JdbcMessageHandler 公开了一个 usePayloadAsParameterSource 标志,用于指示是否应将整个消息作为参数源输入传递。

如果输入通道是直接通道,出站适配器将在与消息发送者相同的线程(以及相同的事务,如果存在的话)中运行其查询。

使用 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 模块提供了用于配置执行环境(例如 ConnectionCallbackPreparedStatementCreator)和操作参数值(例如 SqlParameterSource)的 API。它甚至能够访问用于底层操作的 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 配置的角度来看,<int-jdbc:outbound-channel-adapter> 组件提供了 prepared-statement-setter 属性。该属性允许您指定一个 MessagePreparedStatementSetter bean 引用。

批量更新

从版本 5.1 开始,如果请求消息的负载是一个 Iterable 实例,JdbcMessageHandler 会执行 JdbcOperations.batchUpdate()。如果 Iterable 的每个元素本身不是 Message,则会使用请求消息的头部信息将其包装成 Message。在基于常规 SqlParameterSourceFactory 的配置中,这些消息用于构建 SqlParameterSource[],作为上述 JdbcOperations.batchUpdate() 函数的参数。当应用 MessagePreparedStatementSetter 配置时,会使用 BatchPreparedStatementSetter 变体来遍历每个项目的这些消息,并针对它们调用提供的 MessagePreparedStatementSetter。当选择 keysGenerated 模式时,不支持批量更新。