Streaming queries

The following findEach, findIterate, and findStream are all streaming queries with mainly a difference in terms of style using a closure, iterator or stream. With these streaming queries we can process very large results without having to hold all results in memory. findEach would be the recommended style as it ensures all the resources held by the query are closed without necessitating a try with resources block. Using findStream and findIterate are equally fine with the note that we need to ensure the QueryIterator and Stream are closed typically via try with resources.

From Ebean version 12.3.5 these streaming queries use an "adaptive persistence context". This means that findEach, findIterate, and findStream will work equally as well against small query results as findList. After 1000 beans are processed the queries adapt the persistence context they use to ensure it does not hold all the beans (and run out of memory when processing very large results).

Queries processing less than 1000 beans use a single normal persistence context. After 1000 beans the persistence context adapts such that it does not hold all the beans.

findEach

Execute the query processing the result one bean at a time.

new QCustomer()
 .status.equalTo(Status.NEW)
 .order().id.asc()
 .findEach((Customer customer) -> {

   // do something with customer
   System.out.println("-- visit " + customer);
 });

findEachWhile

Like findEach but takes a predicate that allows us to stop processing the result early.

// Returning false inside the predicate will stop the execution
new QCustomer()
   .status.equalTo(Status.NEW)
   .order().id.asc()
   .findEachWhile((Customer customer) -> {
     // do something with customer
     ...
     // return true to continue processing or false to stop
     return (customer.getId() < 40);
   });

findEach with batch consumer

Similar to findEach but batches the beans up so that they can be processed in batches - for example process in batches of 100 at a time.

new QCustomer()
 .status.equalTo(Status.NEW)
 .order().id.asc()
 .findEach(100, batch -> {
   // process the customers in batches of 100 at a time
   // where batch is List<Customer>
   ...
 });

findStream

Execute the query processing the result as a stream. Use a try with resources block to ensure the resources held by the stream are closed.

try (Stream<Customer> stream =
  new QCustomer()
   .status.equalTo(Status.NEW)
   .order().id.asc()
   .findStream()) {

  stream
    .filter(...)
    .map(..)
    .collect(...);
}

findIterate

Execute the query processing the result as a QueryIterator. Use a try with resources block to ensure the resources held by the iterator are closed.

try (QueryIterator<Customer> it =
  new QCustomer()
   .status.equalTo(Status.NEW)
   .order().id.asc()
   .findIterate()) {

   while (it.hasNext()) {
     Customer customer = it.next();
     ...
   }
}