Module storm.java

Class QueryBuilder<T extends Data,R,ID>

java.lang.Object
st.orm.template.QueryBuilder<T,R,ID>
Type Parameters:
T - the type of the table being queried.
R - the type of the result.
ID - the type of the primary key.

public abstract class QueryBuilder<T extends Data,R,ID> extends Object
QueryBuilder relies on preview features of the Java platform:
Programs can only use QueryBuilder when preview features are enabled.
Preview features may be removed in a future release, or upgraded to permanent features of the Java platform.
A fluent builder for constructing type-safe SELECT and DELETE queries using the entity graph and metamodel.

The QueryBuilder provides a composable, chainable API for building SQL queries without writing raw SQL. It supports joins, WHERE clauses with type-safe metamodel paths, GROUP BY, HAVING, ORDER BY, LIMIT/OFFSET, row locking (FOR SHARE/FOR UPDATE), and result retrieval as streams, lists, or single results.

Instances are obtained from an EntityRepository or ProjectionRepository via their select(), selectCount(), or delete() methods, or from a QueryTemplate via selectFrom() and deleteFrom().

Example: Select with type-safe WHERE clause


 List<User> users = userRepository
         .select()
         .where(User_.address.city.name, EQUALS, "Sunnyvale")
         .orderBy(User_.email)
         .limit(10)
         .getResultList();
 

Example: Delete with WHERE clause


 int deleted = userRepository
         .delete()
         .where(User_.email, IS_NULL)
         .executeUpdate();
 

Example: Join and subquery


 List<User> users = userRepository
         .select()
         .innerJoin(Order.class).on(User.class)
         .where(predicate -> predicate
             .where(User_.active, EQUALS, true)
             .and(predicate.where(Order_.total, GREATER_THAN, 100)))
         .getResultList();
 

Immutability

QueryBuilder is immutable: every builder method (such as where(), orderBy(), limit(), etc.) returns a new instance with the modification applied, leaving the original unchanged. If you call a builder method and ignore the return value, the change is silently lost.


 // WRONG - the where clause is lost because the return value is discarded:
 var builder = userRepository.select();
 builder.where(User_.active, EQUALS, true);  // returns a new builder, but it's ignored
 builder.getResultList();                     // executes without the WHERE clause

 // CORRECT - chain the calls or capture the returned builder:
 var results = userRepository.select()
         .where(User_.active, EQUALS, true)
         .getResultList();
 
See Also:
  • Constructor Details

    • QueryBuilder

      public QueryBuilder()
  • Method Details

    • typed

      public abstract <X> QueryBuilder<T,R,X> typed(@Nonnull Class<X> pkType)
      Returns a typed query builder for the specified primary key type.
      Type Parameters:
      X - the type of the primary key.
      Parameters:
      pkType - the primary key type.
      Returns:
      the typed query builder.
      Throws:
      PersistenceException - if the pk type is not valid.
      Since:
      1.2
    • unsafe

      public abstract QueryBuilder<T,R,ID> unsafe()
      Returns a query builder that allows UPDATE and DELETE queries without a WHERE clause.

      By default, Storm rejects UPDATE and DELETE queries that lack a WHERE clause, throwing a PersistenceException. Call this method to disable that check when you intentionally want to affect all rows in the table.

      Since:
      1.2
    • distinct

      public abstract QueryBuilder<T,R,ID> distinct()
      Marks the current query as a distinct query.
      Returns:
      the query builder.
    • crossJoin

      public abstract QueryBuilder<T,R,ID> crossJoin(@Nonnull Class<? extends Data> relation)
      Adds a cross join to the query.
      Parameters:
      relation - the relation to join.
      Returns:
      the query builder.
    • innerJoin

      public abstract TypedJoinBuilder<T,R,ID> innerJoin(@Nonnull Class<? extends Data> relation)
      Adds an inner join to the query.
      Parameters:
      relation - the relation to join.
      Returns:
      the query builder.
    • leftJoin

      public abstract TypedJoinBuilder<T,R,ID> leftJoin(@Nonnull Class<? extends Data> relation)
      Adds a left join to the query.
      Parameters:
      relation - the relation to join.
      Returns:
      the query builder.
    • rightJoin

      public abstract TypedJoinBuilder<T,R,ID> rightJoin(@Nonnull Class<? extends Data> relation)
      Adds a right join to the query.
      Parameters:
      relation - the relation to join.
      Returns:
      the query builder.
    • join

      public abstract TypedJoinBuilder<T,R,ID> join(@Nonnull JoinType type, @Nonnull Class<? extends Data> relation, @Nonnull String alias)
      Adds a join of the specified type to the query.
      Parameters:
      type - the type of the join (e.g., INNER, LEFT, RIGHT).
      relation - the relation to join.
      alias - the alias to use for the joined relation.
      Returns:
      the query builder.
    • crossJoin

      public abstract QueryBuilder<T,R,ID> crossJoin(@Nonnull StringTemplatePREVIEW template)
      Adds a cross join to the query.
      Parameters:
      template - the condition to join.
      Returns:
      the query builder.
    • innerJoin

      public abstract JoinBuilder<T,R,ID> innerJoin(@Nonnull StringTemplatePREVIEW template, @Nonnull String alias)
      Adds an inner join to the query.
      Parameters:
      template - the condition to join.
      alias - the alias to use for the joined relation.
      Returns:
      the query builder.
    • leftJoin

      public abstract JoinBuilder<T,R,ID> leftJoin(@Nonnull StringTemplatePREVIEW template, @Nonnull String alias)
      Adds a left join to the query.
      Parameters:
      template - the template to join.
      alias - the alias to use for the joined relation.
      Returns:
      the query builder.
    • rightJoin

      public abstract JoinBuilder<T,R,ID> rightJoin(@Nonnull StringTemplatePREVIEW template, @Nonnull String alias)
      Adds a right join to the query.
      Parameters:
      template - the template to join.
      alias - the alias to use for the joined relation.
      Returns:
      the query builder.
    • join

      public abstract JoinBuilder<T,R,ID> join(@Nonnull JoinType type, @Nonnull StringTemplatePREVIEW template, @Nonnull String alias)
      Adds a join of the specified type to the query using a template.
      Parameters:
      type - the join type.
      template - the template to join.
      alias - the alias to use for the joined relation.
      Returns:
      the query builder.
    • join

      public abstract JoinBuilder<T,R,ID> join(@Nonnull JoinType type, @Nonnull QueryBuilder<?,?,?> subquery, @Nonnull String alias)
      Adds a join of the specified type to the query using a subquery.
      Parameters:
      type - the join type.
      subquery - the subquery to join.
      alias - the alias to use for the joined relation.
      Returns:
      the query builder.
    • where

      public final QueryBuilder<T,R,ID> where(@Nonnull ID id)
      Adds a WHERE clause that matches the specified primary key of the table.
      Parameters:
      id - the id to match.
      Returns:
      the query builder.
    • where

      public final QueryBuilder<T,R,ID> where(@Nonnull Ref<T> ref)
      Adds a WHERE clause that matches the specified primary key of the table, expressed by a ref.
      Parameters:
      ref - the ref to match.
      Returns:
      the query builder.
      Since:
      1.3
    • where

      public final QueryBuilder<T,R,ID> where(@Nonnull T record)
      Adds a WHERE clause that matches the specified record.
      Parameters:
      record - the record to match.
      Returns:
      the query builder.
    • whereId

      public final QueryBuilder<T,R,ID> whereId(@Nonnull Iterable<? extends ID> it)
      Adds a WHERE clause that matches the specified primary keys of the table.
      Parameters:
      it - ids to match.
      Returns:
      the query builder.
      Since:
      1.2
    • whereRef

      public final QueryBuilder<T,R,ID> whereRef(@Nonnull Iterable<? extends Ref<T>> it)
      Adds a WHERE clause that matches the specified primary keys of the table, expressed by a ref.
      Parameters:
      it - refs to match.
      Returns:
      the query builder.
      Since:
      1.3
    • where

      public final <V extends Record> QueryBuilder<T,R,ID> where(@Nonnull Metamodel<T,V> path, @Nonnull V record)
      Adds a WHERE clause that matches the specified record. The record can represent any of the related tables in the table graph.
      Parameters:
      path - the path to the object in the table graph.
      record - the records to match.
      Returns:
      the predicate builder.
    • where

      public final <V extends Data> QueryBuilder<T,R,ID> where(@Nonnull Metamodel<T,V> path, @Nonnull Ref<V> ref)
      Adds a WHERE clause that matches the specified ref. The ref can represent any of the related tables in the table graph.
      Parameters:
      path - the path to the object in the table graph.
      ref - the ref to match.
      Returns:
      the predicate builder.
      Since:
      1.3
    • where

      public final <V extends Data> QueryBuilder<T,R,ID> where(@Nonnull Metamodel<T,V> path, @Nonnull Iterable<V> it)
      Adds a WHERE clause that matches the specified records. The records can represent any of the related tables in the table graph.
      Parameters:
      path - the path to the object in the table graph.
      it - the records to match.
      Returns:
      the predicate builder.
    • whereRef

      public final <V extends Data> QueryBuilder<T,R,ID> whereRef(@Nonnull Metamodel<T,V> path, @Nonnull Iterable<? extends Ref<V>> it)
      Adds a WHERE clause that matches the specified records. The records can represent any of the related tables in the table graph.
      Parameters:
      path - the path to the object in the table graph.
      it - the records to match.
      Returns:
      the predicate builder.
      Since:
      1.3
    • where

      public final QueryBuilder<T,R,ID> where(@Nonnull Iterable<? extends T> it)
      Adds a WHERE clause that matches the specified records.
      Parameters:
      it - the records to match.
      Returns:
      the query builder.
    • where

      public final <V> QueryBuilder<T,R,ID> where(@Nonnull Metamodel<T,V> path, @Nonnull Operator operator, @Nonnull Iterable<? extends V> it)
      Adds a WHERE clause that matches the specified objects at the specified path in the table graph.
      Type Parameters:
      V - the type of the object that the metamodel represents.
      Parameters:
      path - the path to the object in the table graph.
      operator - the operator to use for the comparison.
      it - the objects to match, which can be primary keys, records representing the table, or fields in the table graph.
      Returns:
      the query builder.
      Since:
      1.2
    • where

      @SafeVarargs public final <V> QueryBuilder<T,R,ID> where(@Nonnull Metamodel<T,V> path, @Nonnull Operator operator, @Nonnull V... o)
      Adds a WHERE clause that matches the specified objects at the specified path in the table graph.
      Type Parameters:
      V - the type of the object that the metamodel represents.
      Parameters:
      path - the path to the object in the table graph.
      operator - the operator to use for the comparison.
      o - the object(s) to match, which can be primary keys, records representing the table, or fields in the table graph.
      Returns:
      the query builder.
      Since:
      1.2
    • where

      public final QueryBuilder<T,R,ID> where(@Nonnull StringTemplatePREVIEW template)
      Adds a WHERE clause to the query for the specified expression.
      Parameters:
      template - the expression.
      Returns:
      the query builder.
    • where

      public abstract QueryBuilder<T,R,ID> where(@Nonnull Function<WhereBuilder<T,R,ID>,PredicateBuilder<T,?,?>> predicate)
      Adds a WHERE clause to the query using a WhereBuilder.
      Parameters:
      predicate - the predicate to add.
      Returns:
      the query builder.
    • whereAny

      public abstract QueryBuilder<T,R,ID> whereAny(@Nonnull Function<WhereBuilder<T,R,ID>,PredicateBuilder<?,?,?>> predicate)
      Adds a WHERE clause to the query using a WhereBuilder.
      Parameters:
      predicate - the predicate to add.
      Returns:
      the query builder.
    • groupBy

      @SafeVarargs public final QueryBuilder<T,R,ID> groupBy(@Nonnull Metamodel<T,?>... path)
      Adds a GROUP BY clause to the query for field at the specified path in the table graph. The metamodel can refer to manually added joins.
      Parameters:
      path - the path to group by.
      Returns:
      the query builder.
      Since:
      1.2
    • groupByAny

      public final QueryBuilder<T,R,ID> groupByAny(@Nonnull Metamodel<?,?>... path)
      Adds a GROUP BY clause to the query for field at the specified path in the table graph. The metamodel can refer to manually added joins.
      Parameters:
      path - the path to group by.
      Returns:
      the query builder.
      Since:
      1.2
    • groupBy

      public abstract QueryBuilder<T,R,ID> groupBy(@Nonnull StringTemplatePREVIEW template)
      Adds a GROUP BY clause to the query using a string template. Multiple calls to this method append additional columns to the GROUP BY clause.
      Parameters:
      template - the template to group by.
      Returns:
      the query builder.
      Since:
      1.2
    • having

      @SafeVarargs public final <V> QueryBuilder<T,R,ID> having(@Nonnull Metamodel<T,V> path, @Nonnull Operator operator, @Nonnull V... o)
      Adds a HAVING clause to the query using the specified expression.
      Parameters:
      path - the path to the object in the table graph.
      operator - the operator to use for the comparison.
      o - the object(s) to match, which can be primary keys, records representing the table, or fields in the table graph.
      Returns:
      the query builder.
      Since:
      1.2
    • havingAny

      @SafeVarargs public final <V> QueryBuilder<T,R,ID> havingAny(@Nonnull Metamodel<?,V> path, @Nonnull Operator operator, @Nonnull V... o)
      Adds a HAVING clause to the query using the specified expression. The metamodel can refer to manually added joins.
      Parameters:
      path - the path to the object in the table graph.
      operator - the operator to use for the comparison.
      o - the object(s) to match, which can be primary keys, records representing the table, or fields in the table graph or manually added joins.
      Returns:
      the query builder.
      Since:
      1.2
    • having

      public abstract QueryBuilder<T,R,ID> having(@Nonnull StringTemplatePREVIEW template)
      Adds a HAVING clause to the query using the specified expression. Multiple calls to this method are combined using AND.
      Parameters:
      template - the expression to add.
      Returns:
      the query builder.
      Since:
      1.2
    • orderBy

      @SafeVarargs public final QueryBuilder<T,R,ID> orderBy(@Nonnull Metamodel<T,?>... path)
      Adds an ORDER BY clause to the query for the field at the specified path in the table graph.
      Parameters:
      path - the path to order by.
      Returns:
      the query builder.
      Since:
      1.2
    • orderByDescending

      public final QueryBuilder<T,R,ID> orderByDescending(@Nonnull Metamodel<T,?> path)
      Adds an ORDER BY clause to the query for the field at the specified path in the table graph. The results are sorted in descending order.
      Parameters:
      path - the path to order by.
      Returns:
      the query builder.
      Since:
      1.2
    • orderByDescending

      @SafeVarargs public final QueryBuilder<T,R,ID> orderByDescending(@Nonnull Metamodel<T,?>... path)
      Adds an ORDER BY clause to the query for the fields at the specified paths in the table graph. The results are sorted in descending order for each column.
      Parameters:
      path - the paths to order by.
      Returns:
      the query builder.
      Since:
      1.9
    • orderByDescendingAny

      public final QueryBuilder<T,R,ID> orderByDescendingAny(@Nonnull Metamodel<?,?> path)
      Adds an ORDER BY clause to the query for the field at the specified path in the table graph or manually added joins. The results are sorted in descending order.
      Parameters:
      path - the path to order by.
      Returns:
      the query builder.
      Since:
      1.9
    • orderByDescendingAny

      public final QueryBuilder<T,R,ID> orderByDescendingAny(@Nonnull Metamodel<?,?>... path)
      Adds an ORDER BY clause to the query for the fields at the specified paths in the table graph or manually added joins. The results are sorted in descending order for each column.
      Parameters:
      path - the paths to order by.
      Returns:
      the query builder.
      Since:
      1.9
    • orderByDescending

      public final QueryBuilder<T,R,ID> orderByDescending(@Nonnull StringTemplatePREVIEW template)
      Adds an ORDER BY clause to the query using a string template. The results are sorted in descending order. Multiple calls to this method append additional columns to the ORDER BY clause.
      Parameters:
      template - the template to order by.
      Returns:
      the query builder.
      Since:
      1.9
    • orderByAny

      public final QueryBuilder<T,R,ID> orderByAny(@Nonnull Metamodel<?,?>... path)
      Adds an ORDER BY clause to the query for the field at the specified path in the table graph or manually added joins.
      Parameters:
      path - the path to order by.
      Returns:
      the query builder.
      Since:
      1.2
    • orderBy

      public abstract QueryBuilder<T,R,ID> orderBy(@Nonnull StringTemplatePREVIEW template)
      Adds an ORDER BY clause to the query using a string template. Multiple calls to this method append additional columns to the ORDER BY clause.
      Parameters:
      template - the template to order by.
      Returns:
      the query builder.
      Since:
      1.2
    • hasOrderBy

      protected abstract boolean hasOrderBy()
      Returns true if any ORDER BY columns have been added to this query builder.
      Returns:
      true if ORDER BY columns are present, false otherwise.
      Since:
      1.9
    • limit

      public abstract QueryBuilder<T,R,ID> limit(int limit)
      Adds a LIMIT clause to the query.
      Parameters:
      limit - the maximum number of records to return.
      Returns:
      the query builder.
      Since:
      1.2
    • offset

      public abstract QueryBuilder<T,R,ID> offset(int offset)
      Adds an OFFSET clause to the query.
      Parameters:
      offset - the offset.
      Returns:
      the query builder.
      Since:
      1.2
    • append

      public abstract QueryBuilder<T,R,ID> append(@Nonnull StringTemplatePREVIEW template)
      Append the query with a string template.
      Parameters:
      template - the string template to append.
      Returns:
      the query builder.
    • forShare

      public abstract QueryBuilder<T,R,ID> forShare()
      Locks the selected rows for reading.
      Returns:
      the query builder.
      Throws:
      PersistenceException - if the database does not support the specified lock mode, or if the lock mode is not supported for the current query.
      Since:
      1.2
    • forUpdate

      public abstract QueryBuilder<T,R,ID> forUpdate()
      Locks the selected rows for reading.
      Returns:
      the query builder.
      Throws:
      PersistenceException - if the database does not support the specified lock mode, or if the lock mode is not supported for the current query.
      Since:
      1.2
    • forLock

      public abstract QueryBuilder<T,R,ID> forLock(@Nonnull StringTemplatePREVIEW template)
      Locks the selected rows using a custom lock mode.

      Note: This method results in non-portable code, as the lock mode is specific to the underlying database.

      Returns:
      the query builder.
      Throws:
      PersistenceException - if the lock mode is not supported for the current query.
      Since:
      1.2
    • build

      public abstract Query build()
      Builds the query based on the current state of the query builder.
      Returns:
      the constructed query.
    • prepare

      public final PreparedQuery prepare()
      Prepares the query for execution.

      Unlike regular queries, which are constructed lazily, prepared queries are constructed eagerly. Prepared queries allow the use of bind variables and enable reading generated keys after row insertion.

      Note: The prepared query must be closed after usage to prevent resource leaks. As the prepared query is AutoCloseable, it is recommended to use it within a try-with-resources block.

      Returns:
      the prepared query.
      Throws:
      PersistenceException - if the query preparation fails.
    • page

      public final Page<R> page(int pageNumber, int pageSize)
      Executes the query and returns a Page of results using offset-based pagination.

      This method executes two queries: one to count the total number of matching results (without offset or limit), and one to fetch the content for the requested page. The caller is responsible for adding ORDER BY clauses to ensure deterministic ordering across pages.

      Page numbers are zero-based: pass 0 for the first page.

      Parameters:
      pageNumber - the zero-based page index (must not be negative).
      pageSize - the maximum number of results per page (must be positive).
      Returns:
      a page containing the results and pagination metadata.
      Throws:
      IllegalArgumentException - if pageNumber is negative or pageSize is not positive.
      Since:
      1.10
    • page

      public final Page<R> page(@Nonnull Pageable pageable)
      Executes the query and returns a Page of results using offset-based pagination.

      This method executes two queries: one to count the total number of matching results (without offset or limit), and one to fetch the content for the requested page. Sort orders can be specified either through the pageable or through explicit orderBy calls on the query builder, but not both. If both are present, a PersistenceException is thrown.

      Use Pageable.ofSize(int) for the first page, then navigate with Page.nextPageable() or Page.previousPageable().

      Parameters:
      pageable - the pagination request specifying page number and page size.
      Returns:
      a page containing the results and pagination metadata.
      Throws:
      PersistenceException - if the pageable has sort orders and the query builder has explicit orderBy calls.
      Since:
      1.10
    • page

      public final Page<R> page(@Nonnull Pageable pageable, long totalCount)
      Executes the query and returns a Page of results using offset-based pagination with a pre-computed total count.

      This method applies the sort orders from the pageable, then fetches the content for the requested page using the provided total count instead of executing a separate count query. This is useful when the total count is already known (for example, cached from a previous request or obtained from an external source), avoiding a redundant COUNT query.

      Sort orders can be specified either through the pageable or through explicit orderBy calls on the query builder, but not both. If both are present, a PersistenceException is thrown.

      Parameters:
      pageable - the pagination request specifying page number and page size.
      totalCount - the pre-computed total number of matching results.
      Returns:
      a page containing the results and pagination metadata.
      Throws:
      PersistenceException - if the pageable has sort orders and the query builder has explicit orderBy calls.
      Since:
      1.10
    • scroll

      public abstract Window<R> scroll(int size)
      Executes the query and returns a Window of results.

      This method fetches size + 1 rows to determine whether more results are available, then returns at most size results along with a hasNext flag. The caller is responsible for managing any WHERE and ORDER BY clauses externally.

      The returned window does not carry navigation tokens (next() and previous() return null).

      Parameters:
      size - the maximum number of results to include in the window (must be positive).
      Returns:
      a window containing the results and a flag indicating whether more results exist.
      Throws:
      IllegalArgumentException - if size is not positive.
      Since:
      1.11
    • scroll

      public abstract Window<R> scroll(@Nonnull Scrollable<T> scrollable)
      Executes a scroll request from a Scrollable token, typically obtained from Window.next() or Window.previous().
      Parameters:
      scrollable - the scroll request containing cursor state, key, sort, size, and direction.
      Returns:
      a window containing the results and navigation tokens.
      Since:
      1.11
    • getResultStream

      public abstract Stream<R> getResultStream()
      Executes the query and returns a stream of results.

      The resulting stream is lazily loaded, meaning that the records are only retrieved from the database as they are consumed by the stream. This approach is efficient and minimizes the memory footprint, especially when dealing with large volumes of records.

      Note: Calling this method does trigger the execution of the underlying query, so it should only be invoked when the query is intended to run. Since the stream holds resources open while in use, it must be closed after usage to prevent resource leaks. As the stream is AutoCloseable, it is recommended to use it within a try-with-resources block.

      Returns:
      a stream of results.
      Throws:
      PersistenceException - if the query operation fails due to underlying database issues, such as connectivity.
    • getResultCount

      public final long getResultCount()
      Returns the number of results of this query.
      Returns:
      the total number of results of this query as a long value.
      Throws:
      PersistenceException - if the query operation fails due to underlying database issues, such as connectivity.
    • getResultList

      public final List<R> getResultList()
      Executes the query and returns a list of results.
      Returns:
      the list of results.
      Throws:
      PersistenceException - if the query fails.
    • getSingleResult

      public final R getSingleResult()
      Executes the query and returns a single result.
      Returns:
      the single result.
      Throws:
      NoResultException - if there is no result.
      NonUniqueResultException - if more than one result.
      PersistenceException - if the query fails.
    • getOptionalResult

      public final Optional<R> getOptionalResult()
      Executes the query and returns an optional result.
      Returns:
      the optional result.
      Throws:
      NonUniqueResultException - if more than one result.
      PersistenceException - if the query fails.
    • executeUpdate

      public final int executeUpdate()
      Execute a DELETE statement.
      Returns:
      the number of rows impacted as result of the statement.
      Throws:
      PersistenceException - if the statement fails.