首页 > 解决方案 > 如何在控制器层Spring进行流式传输

问题描述

我正在开发一个简单的 Java Spring 应用程序并尝试从数据库中获取所有保存的订单。尽管数据库中有大约 100k 条记录,但应用程序流程很简单。我在 JPA 层使用了从 DB 到应用程序数据的流式传输,效果很好。但是当这开始从控制器传输到客户端时,它会抛出 502 响应。以下是所有三层的代码。

有人可以帮助解决这个问题吗?要求所有这些数据都应该不使用任何过滤器或分页。

@GetMapping(produces = "application/json")
    @ResponseStatus(HttpStatus.OK)
    public Resources getOrders() {
        return userService.getOrders();
    }

    @Transactional
    public Resources getOrders() {
        Stream<Orders> orders = streamAll();

        List<Order> orderList = new ArrayList<>();
        orderList.addAll(orderConverter.createFromEntities(orders));

        Resource resources = new Resource();
        resources.setResources(orderList);
        return resources;
    }

    @Query("select u from Order")
    Stream<Orders> streamAll();

标签: javaspring

解决方案


您收到 502 响应,因为您的 JVM 内存已耗尽。在存储库层使用流式分离对象。

首先,检查您的数据库是否支持通过流数据获取结果集。例如,MYSQL 支持它使用只读会话并使用值 Integer.MIN_VALUE [1] 获取大小。

稍后,自定义您的存储库以使用 StatelessSession 接口 [2] 打开无状态会话。多亏了这个接口,您可以分离对象,因此 Hibernate 会立即释放它,并且不会占用缓存中的内存。

如果您需要进行更改,请再次记住,您的对象是分离的。您需要打开另一个会话(读写)。您的对象将与这些会话一起保存,保存后,您必须手动将其分离。

此示例涵盖了第一个要求:

@PersistenceContext
private EntityManager entityManager;

public void stackoverflowQuestion() {
        Session currentSession = entityManager.unwrap(Session.class);
        ScrollableResults rows = currentSession
            .createQuery("SELECT u FROM Order", Orders.class)
            .setFetchSize(Integer.MIN_VALUE)
            .scroll(ScrollMode.FORWARD_ONLY);

        while (rows.next()) {
            Orders order = (Orders) rows.get(0);
                ...
            }
}

[1] https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-reference-implementation-notes.html

[2] https://docs.jboss.org/hibernate/orm/3.5/reference/en-US/html/batch.html


推荐阅读