这也可以通过 Spring MVC Controller 来完成,但是有几个问题:Spring Data JPA Repository 的限制,数据库是否支持 Holdable Cursors(ResultSet Holdability)以及 Jackson 的版本。
我难以理解的关键概念是,Java 8 Stream 返回一系列函数,这些函数在终端操作中执行,因此数据库必须在执行终端操作。
Spring Data JPA 限制
我发现 Spring Data JPA 文档没有为 Java 8 Streams 提供足够的详细信息。看起来您可以简单地声明 Stream<MyObject> readAll(),但我需要用 @Query 注释该方法以使其工作。我也无法使用 JPA 标准 API Specification。所以我不得不接受一个硬编码的查询,比如:
@Query("select mo from MyObject mo where mo.foo.id in :fooIds")
Stream<MyObject> readAllByFooIn(@Param("fooIds") Long[] fooIds);
可保持光标
如果您有一个支持可保持游标的数据库,则在提交事务后可以访问结果集。这一点很重要,因为我们通常用@Transactional 注释我们的@Service 类方法,因此如果您的数据库支持可保持游标,则可以在服务方法返回后访问ResultSet,即在@Controller 方法中。如果数据库不支持可保持游标,例如MySQL,您需要将@Transaction 注解添加到控制器的@RequestMapping 方法中。
所以现在 ResultSet 可以在 @Service 方法之外访问,对吧?这又取决于可持性。对于 MySQL,它只能在 @Transactional 方法中访问,因此以下将起作用(尽管违背了使用 Java 8 Streams 的全部目的):
@Transaction @RequestMapping(...)
public List<MyObject> getAll() {
try(Stream<MyObject> stream = service.streamAll) {
return stream.collect(Collectors.toList())
};
}
但不是
@Transaction @RequestMapping
public Stream<MyObject> getAll() {
return service.streamAll;
}
因为 终端操作符 在您的@Controller 中不是,它会在控制器方法返回后在 Spring 中发生。
在不支持可保持游标的情况下将流序列化为 JSON
要将流序列化为没有可保持游标的 JSON,请将 HttpServletResponse response 添加到控制器方法中,获取输出流并使用 ObjectMapper 写入流。使用 FasterXML 3.x,您可以调用 ObjectMapper().writeValue(writer, stream),但使用 2.8.x,您必须使用流的迭代器:
@RequestMapping(...)
@Transactional
public void getAll(HttpServletResponse response) throws IOException {
try(final Stream<MyObject> stream = service.streamAll()) {
final Writer writer = new BufferedWriter(new OutputStreamWriter(response.getOutputStream()));
new ObjectMapper().writerFor(Iterator.class).writeValue(writer, stream.iterator());
}
}
后续步骤
我接下来的步骤是尝试在 Callable WebAsyncTask 中重构它,并将 JSON 序列化移动到服务中。
参考文献