如何从表中读取所有记录(> 1000万条记录)并将每条记录作为块响应提供?

时间:2016-07-25 14:51:48

标签: java mysql hibernate playframework akka

我尝试使用hibernate的可滚动结果从数据库中获取记录,并参考this github项目,我尝试将每条记录作为块响应发送。

控制器:

@Transactional(readOnly=true)
public Result fetchAll() {
    try {
        final Iterator<String> sourceIterator = Summary.fetchAll();
        response().setHeader("Content-disposition", "attachment; filename=Summary.csv");

        Source<String, ?> s =  Source.from(() -> sourceIterator);
        return ok().chunked(s.via(Flow.of(String.class).map(i -> ByteString.fromString(i+"\n")))).as(Http.MimeTypes.TEXT);

    } catch (Exception e) {
        return badRequest(e.getMessage());
    }
}

服务

 public static Iterator<String> fetchAll() {
    StatelessSession session = ((Session) JPA.em().getDelegate()).getSessionFactory().openStatelessSession();
    org.hibernate.Query query = session.createQuery("select l.id from Summary l")
            .setFetchSize(Integer.MIN_VALUE).setCacheable(false).setReadOnly(true);
    ScrollableResults results = query.scroll(ScrollMode.FORWARD_ONLY);

    return new models.ScrollableResultIterator<>(results, String.class);
}

迭代器:

public class ScrollableResultIterator<T> implements Iterator<T> {
   private final ScrollableResults results;
   private final Class<T> type;

   public ScrollableResultIterator(ScrollableResults results, Class<T> type) {
      this.results = results;
      this.type = type;
   }

   @Override
   public boolean hasNext() {
      return results.next();
   }

   @Override
   public T next() {
      return type.cast(results.get(0));
   }
}

出于测试目的,我在我的表中有1007条记录,每当我调用此终点时,它总是只返回503条记录。

启用AKKA日志级别到DEBUG并再次尝试,它将以下行记录1007次   2016-07-25 19:55:38 +0530 [DEBUG] from org.hibernate.loader.Loader in application-akka.actor.default-dispatcher-73 - Result row:从日志中我确认它取出所有内容,但无法得到剩下的内容。

我在我的工作台中运行相同的查询,然后将其导出到本地文件,并将其与终点生成的文件进行比较,保留从终点生成的LHS记录和从Workbench导出的RHS文件。  第一排比赛,第二排和第三排没有匹配。之后,它会获得备用记录的匹配,直到结束。

enter image description here

请纠正我,如果我做错了什么并建议我这是为大型数据库记录生成CSV的正确方法。

为了测试,我删除了上面代码段中的CSV转换逻辑。

1 个答案:

答案 0 :(得分:0)

// Controller code
// Prepare a chunked text stream
ExportAsChuncked eac = new ExportAsChuncked();
response().setHeader("Content-disposition","attachment; filename=results.csv");
Chunks<String> chunks = new StringChunks() {

    // Called when the stream is ready
    public void onReady(Chunks.Out<String> out) {
        try {
            eac.exportData(scrollableIterator, out);
        }catch (XOException e) {
            Logger.error(ERROR_WHILE_DOWNLOADING_RESPONSE, e);
        }
        out.close();
    }

};
// Serves this stream with 200 OK
return ok(chunks);


// Export as chunk logic
class ExportAsChuncked {
     void exportData(Iterator<String> data, Chunks.Out<String> out) {
            while(data.hasNext()) {
                  out.write(data.next());
            }
     }
}