出站通道适配器
出站通道适配器与入站通道适配器相反:它的作用是处理消息并用它来执行 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 来获取不同的行为。 |
出站适配器需要引用 DataSource
或 JdbcTemplate
。你也可以注入 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 来配置执行环境(例如 ConnectionCallback
或 PreparedStatementCreator
)和操作参数值(例如 SqlParameterSource
)。它甚至可以访问低级操作的 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 配置的角度来看,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
模式时,不支持批量更新。