gpt4 book ai didi

java - 使用 Spring MVC 流式传输可关闭资源

转载 作者:搜寻专家 更新时间:2023-11-01 00:56:09 24 4
gpt4 key购买 nike

阅读后this article ,我希望使用 Spring 将数据库查询结果直接流式传输到 JSON 响应,以确保常量内存使用(不会贪婪地加载内存中的 List)。

与文章中使用 Hibernate 所做的类似,我组装了一个 greetingRepository 对象,该对象返回基于 JdbcTemplate 的数据库内容流。在该实现中,我在查询的 ResultSet 上创建了一个迭代器,并按如下方式返回流:

return StreamSupport.stream(spliterator(), false).onClose(() -> {
log.info("Closing ResultSetIterator stream");
JdbcUtils.closeResultSet(resultSet);
});

即使用 onClose() 方法保证如果在 try-with-resources 结构中声明了流,底层的 ResultSet 将被关闭:

try(Stream<Greeting> stream = greetingRepository.stream()) {
// operate on the stream
} // ResultSet underlying the stream will be guaranteed to be closed

但如文章中所述,我希望此流由自定义对象映射器(文章中定义的增强型 MappingJackson2HttpMessageConverter)使用。如果我们将 try-with-resources 需求放在一边,这是可行的,如下所示:

@RequestMapping(method = GET)
Stream<GreetingResource> stream() {
return greetingRepository.stream().map(GreetingResource::new);
}

然而,正如一位同事在那篇文章底部评论的那样,这并没有考虑关闭底层资源。

在 Spring MVC 的上下文中,我如何才能从数据库一直流式传输到 JSON 响应并仍然保证 ResultSet 将被关闭?您能否提供一个具体的示例解决方案?

最佳答案

您可以创建一个结构来在序列化时推迟查询执行。此构造将以编程方式启动和结束事务。

public class TransactionalStreamable<T> {

private final PlatformTransactionManager platformTransactionManager;

private final Callable<Stream<T>> callable;

public TransactionalStreamable(PlatformTransactionManager platformTransactionManager, Callable<Stream<T>> callable) {
this.platformTransactionManager = platformTransactionManager;
this.callable = callable;
}

public Stream stream() {
TransactionTemplate txTemplate = new TransactionTemplate(platformTransactionManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
txTemplate.setReadOnly(true);

TransactionStatus transaction = platformTransactionManager.getTransaction(txTemplate);

try {
return callable.call().onClose(() -> {
platformTransactionManager.commit(transaction);
});
} catch (Exception e) {
platformTransactionManager.rollback(transaction);
throw new RuntimeException(e);
}
}

public void forEach(Consumer<T> c) {
try (Stream<T> s = stream()){
s.forEach(c);
}
}
}

使用专用的 json 序列化器:

JsonSerializer<?> transactionalStreamableSer = new StdSerializer<TransactionalStreamable<?>>(TransactionalStreamable.class, true) {
@Override
public void serialize(TransactionalStreamable<?> streamable, JsonGenerator jgen, SerializerProvider provider) throws IOException {
jgen.writeStartArray();
streamable.forEach((CheckedConsumer) e -> {
provider.findValueSerializer(e.getClass(), null).serialize(e, jgen, provider);
});

jgen.writeEndArray();
}
};

可以这样使用:

@RequestMapping(method = GET)
TransactionalStreamable<GreetingResource> stream() {
return new TransactionalStreamable(platformTransactionManager , () -> greetingRepository.stream().map(GreetingResource::new));
}

所有的工作都将在 jackson 序列化对象时完成。这可能是或不是关于错误处理的问题(例如,使用 Controller 建议)。

关于java - 使用 Spring MVC 流式传输可关闭资源,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28830096/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com