diff --git a/org.adempiere.base/src/org/compiere/model/Query.java b/org.adempiere.base/src/org/compiere/model/Query.java
index 1cf0bcb392..9ad02eb43e 100644
--- a/org.adempiere.base/src/org/compiere/model/Query.java
+++ b/org.adempiere.base/src/org/compiere/model/Query.java
@@ -28,7 +28,12 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.function.Consumer;
import java.util.logging.Level;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
import org.adempiere.exceptions.DBException;
import org.compiere.util.CLogger;
@@ -645,51 +650,83 @@ public class Query
}
/**
- * Return an Iterator implementation to fetch one PO at a time. The implementation first retrieve
- * all IDS that match the query criteria and issue sql query to fetch the PO when caller want to
- * fetch the next PO. This minimize memory usage but it is slower than the list method.
- * @return Iterator
+ * Return an Iterable implementation that can be used in a for expression. Example:
+ *
{@code
+ * Iterable query = new Query(...).iterable();
+ *
+ * for (MTable table : query) {
+ * // Do stuff with the element
+ * }
+ *
+ *
+ * @return Iterable
* @throws DBException
*/
- public Iterator iterate() throws DBException
+ public Iterable iterable() throws DBException
{
- String[] keys = table.getKeyColumns();
- StringBuilder sqlBuffer = new StringBuilder(" SELECT ");
- for (int i = 0; i < keys.length; i++) {
- if (i > 0)
- sqlBuffer.append(", ");
- if (!joinClauseList.isEmpty())
- sqlBuffer.append(table.getTableName()).append(".");
- sqlBuffer.append(keys[i]);
- }
- sqlBuffer.append(" FROM ").append(table.getTableName());
- String sql = buildSQL(sqlBuffer, true);
-
+ return () -> iterate();
+ }
+
+ /**
+ * Return an Stream implementation to fetch one PO at a time. This method will only create POs on-demand and
+ * they will become eligible for garbage collection once they have been consumed by the stream, so unlike
+ * {@link #list()} it doesn't have to hold a copy of all the POs in the result set in memory at one time.
+ * For situations where you need to iterate over a result set and operate on the results one-at-a-time rather
+ * than operate on the group as a whole, this method is likely to give better performance than list().
+ * @return Stream
+ * @throws DBException
+ */
+ public Stream stream() throws DBException
+ {
+ String sql = buildSQL(null, true);
+
PreparedStatement pstmt = null;
ResultSet rs = null;
- List