如何使用JPA(或至少使用Hibernate)处理大型数据集?

2022-09-02 03:49:52

我需要让我的Web应用程序与非常大的数据集一起工作。目前,我得到的要么是OutOfMemoryException,要么是1-2分钟生成的输出。

让我们简单一点,假设我们在DB中有2个表:第一个表中大约有1000行,第二个表中有10 000 000行。后一个表有几个字段,包括“workerId”和“hoursworked”字段等。我们需要的是:WorkerWorkLog

  1. 计算每个用户的总工作时数;

  2. 每个用户的工作周期列表。

对于纯 SQL 中的每个任务,最直接的方法 (IMO) 是:

1)

select Worker.name, sum(hoursWorked) from Worker, WorkLog 
   where Worker.id = WorkLog.workerId 
   group by Worker.name;

//results of this query should be transformed to Multimap<Worker, Long>

2)

select Worker.name, WorkLog.start, WorkLog.hoursWorked from Worker, WorkLog
   where Worker.id = WorkLog.workerId;

//results of this query should be transformed to Multimap<Worker, Period>
//if it was JDBC then it would be vitally 
//to set resultSet.setFetchSize (someSmallNumber), ~100

所以,我有两个问题:

  1. 如何使用JPA(或至少使用Hibernate)实现我的每种方法;
  2. 你会如何处理这个问题(当然是JPA或Hibernate)?

答案 1

假设我们在 DB 中有 2 个表:Worker 和 WorkLog,第一个表大约有 1000 行,第二个表有 10 000 000 行

对于像这样的高容量,我的建议是使用Hibernate中的The StatelessSession接口

或者,Hibernate 提供了一个面向命令的 API,可用于以分离对象的形式将数据流式传输到数据库或从数据库流式传输数据。A 没有与之关联的持久性上下文,并且不提供许多更高级别的生命周期语义。特别是,无状态会话不实现一级缓存,也不与任何二级缓存或查询缓存交互。它不实现事务性后写或自动脏检查。使用无状态会话执行的操作从不级联到关联的实例。无状态会话忽略集合。通过无状态会话执行的操作会绕过 Hibernate 的事件模型和拦截器。由于缺乏第一级缓存,无状态会话容易受到数据别名效应的影响。无状态会话是一种更靠近底层 JDBC 的低级抽象。StatelessSession

StatelessSession session = sessionFactory.openStatelessSession();
Transaction tx = session.beginTransaction();

ScrollableResults customers = session.getNamedQuery("GetCustomers")
    .scroll(ScrollMode.FORWARD_ONLY);
while ( customers.next() ) {
    Customer customer = (Customer) customers.get(0);
    customer.updateStuff(...);
    session.update(customer);
}

tx.commit();
session.close();

在此代码示例中,查询返回的实例将立即分离。它们从不与任何持久性上下文相关联。Customer

接口定义的 和 操作被视为直接数据库行级操作。它们导致立即执行 SQL 或分别执行。它们与接口定义的操作具有不同的语义。insert(), update()delete()StatelessSessionINSERT, UPDATEDELETEsave(), saveOrUpdate()delete()Session


答案 2

看来你也可以使用EclipseLink做到这一点。检查这个 : http://wiki.eclipse.org/EclipseLink/Examples/JPA/Pagination

Query query = em.createQuery...
query.setHint(QueryHints.CURSOR, true)
     .setHint(QueryHints.SCROLLABLE_CURSOR, true)
ScrollableCursor scrl = (ScrollableCursor)q.getSingleResult();
Object o = null;
while ((o = scrl.next()) != null) { ... }

推荐