出站通道适配器
出站通道适配器是入站通道适配器的反向操作:其职责是处理消息并利用它来执行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 以获得不同的行为。
出站适配器需要一个对 DataSource 或 JdbcTemplate 的引用。你也可以注入一个 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 模块提供了用于配置执行环境(例如 ConnectionCallback 或 PreparedStatementCreator)和操作参数值(例如 SqlParameterSource)的 API。它甚至能够访问用于底层操作的 API,例如 StatementCallback。
从 Spring Integration 4.2 开始,MessagePreparedStatementSetter 允许在 requestMessage 上下文中手动指定 PreparedStatement 的参数。该类的作用与标准 Spring JDBC API 中的 PreparedStatementSetter 完全相同。实际上,当 JdbcMessageHandler 在 JdbcTemplate 上调用 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 模式时,不支持批量更新。