Package com.blazebit.persistence.querydsl
blaze-persistence-integration-querydsl-expressionsmodule implements an extended expression model for Blaze-Persistence JPQL.Next.
The module provides a BlazeJPAQuery as a default implementation of JPQLNextQuery, which extends the all familiar JPQLQuery.
BlazeJPAQuery is analog to JPAQuery. Users can
implement extensions on top of JPQLNextQuery by extending AbstractBlazeJPAQuery.
BlazeJPAQuery can be serialized using the JPQLNextSerializer, and may be rendered to a CriteriaBuilder using the BlazeCriteriaBuilderRenderer.
This allows for the queries to be executed through Blaze-Persistence JPQL.Next query engine. Be sure to use the
JPQLNextTemplates or any Templates implementation that includes the
extensions from JPQLNextTemplates when using JPQL.Next specific features (e.g. window functions, values
clauses, set operations, common table expressions).
This module aims an API that is as close to the original QueryDSL API as possible. Where features did not exist in
querydsl-jpa, but did exist in
querydsl-sql, we stayed as close to the existing SQL implementation as possible. This includes the implementation for window functions, common table expressions (CTEs) and union queries which was the basis for all types of set expressions.
Staying close to QueryDSL's API however, also means that the API is not as fluent as Blaze-Persistence users are accustomed to. This means that creating common table expressions or complex set operations may lead to superfluous code.
Examples
The following chapters demonstrate some of the possibilities of theblaze-persistence-integration-querydsl-expressionsintegration.
Plain query
QTestEntity testEntity = QTestEntity.testEntity;
BlazeJPAQuery<Tuple> query = new BlazeJPAQuery<Tuple>(entityManager, cbf).from(testEntity)
.select(testEntity.field.as("blep"), testEntity.field.substring(2))
.where(testEntity.field.length().gt(1));
List<Tuple> fetch = query.fetch();
Implicit joins
Contrary to JPQL, JPQL.Next allows for implicit joins. For deep path expressions it is not necessary to specify the joins manually.
List<Book> dilbert = new BlazeJPAQuery<>(entityManager, cbf).from(book)
.where(book.author.name.eq("Dilbert"))
.select(book).fetch();
Window functions
QTestEntity sub = new QTestEntity("sub");
BlazeJPAQuery<Tuple> query = new BlazeJPAQuery<Tuple>(entityManager, cbf).from(testEntity)
.select(testEntity.field.as("blep"), JPQLNextExpressions.rowNumber(), JPQLNextExpressions.lastValue(testEntity.field).over().partitionBy(testEntity.id))
.where(testEntity.id.in(select(sub.id).from(sub)));
List<Tuple> fetch = query.fetch();
Named window functions
QTestEntity sub = new QTestEntity("sub");
NamedWindow blep = new NamedWindow("whihi").partitionBy(testEntity.id);
BlazeJPAQuery<Tuple> query = new BlazeJPAQuery<Tuple>(entityManager, cbf).from(testEntity)
.window(blep)
.select(testEntity.field.as("blep"), JPQLNextExpressions.rowNumber().over(blep), JPQLNextExpressions.lastValue(testEntity.field).over(blep))
.where(testEntity.id.in(select(sub.id).from(sub)));
List<Tuple> fetch = query.fetch();
Regular association joins
Map<Author, List<Book>> booksByAuthor = new BlazeJPAQuery<>(entityManager, cbf)
.from(author)
.innerJoin(author.books, book)
.transform(GroupBy.groupBy(author).as(GroupBy.list(book)));
Regular entity joins
QAuthor otherAuthor = new QAuthor("otherAuthor");
QBook otherBook = new QBook("otherBook");
Map<Author, List<Book>> booksByAuthor = new BlazeJPAQuery<Tuple>(entityManager, cbf)
.from(otherAuthor)
.innerJoin(otherBook).on(otherBook.author.eq(otherAuthor))
.transform(GroupBy.groupBy(otherAuthor).as(GroupBy.list(otherBook)));
Managed type values clause
Book theBook = new Book();
theBook.id = 1337L;
theBook.name = "test";
List<Book> fetch = new BlazeJPAQuery<Book>(entityManager, cbf)
.fromValues(book, Collections.singleton(theBook))
.select(book)
.fetch();
Managed attribute values clause
StringPath bookName = Expressions.stringPath("bookName");
List<String> fetch = new BlazeJPAQuery<>(entityManager, cbf)
.fromValues(book.name, bookName, Collections.singleton("book"))
.select(bookName)
.fetch();
Common Table Expressions
First declare your CTE entity:
@CTE
@Entity
public class IdHolderCte {
@Id
Long id;
String name;
}
Next, it can be queried as such:
List<Long> fetch = new BlazeJPAQuery<TestEntity>(entityManager, cbf)
.with(idHolderCte, select(
JPQLNextExpressions.bind(idHolderCte.id, book.id),
JPQLNextExpressions.bind(idHolderCte.name, book.name)).from(book))
.select(idHolderCte.id).from(idHolderCte)
.fetch();
Note: Set operations are also allowed in CTEs, and through set operations it is also possible to write recursive CTEs.
Subquery joins
A limitation of JPQL frequently stumbled opon, is that subqueries cannot be joined. With Blaze-Persistence however, this is perfectly possible:
QRecursiveEntity recursiveEntity = new QRecursiveEntity("t");
List<RecursiveEntity> fetch = new BlazeJPAQuery<>(entityManager, cbf)
.select(recursiveEntity)
.from(JPAExpressions.select(recursiveEntity)
.from(recursiveEntity)
.where(recursiveEntity.parent.name.eq("root1"))
.orderBy(recursiveEntity.name.asc())
.limit(1L), recursiveEntity)
.fetch();
The subquery may project any managed entity, including CTEs.
Lateral joins
Subquery joins are allowed to access outer query variables, if a lateral join is used.
QRecursiveEntity t = new QRecursiveEntity("t");
QRecursiveEntity subT = new QRecursiveEntity("subT");
QRecursiveEntity subT2 = new QRecursiveEntity("subT2");
List<Tuple> fetch = new BlazeJPAQuery<>(entityManager, cbf)
.select(t, subT2)
.from(t)
.leftJoin(JPAExpressions.select(subT).from(t.children, subT).orderBy(subT.id.asc()).limit(1), subT2)
.lateral()
.fetch();
- Author:
- Jan-Willem Gmelig Meyling
-
ClassDescriptionAbstractBlazeJPAQuery<T,
Q extends AbstractBlazeJPAQuery<T, Q>> Abstract base class for JPA API based implementations of the JPQLQuery interfaceBinds<X>FactoryExpressionfor representing CTE bindings.A class for rendering aBlazeJPAQueryto aCriteriaBuilderBlazeJPAQuery is the default implementation of the JPQLQuery interface for Blaze-Persistence JPQL.NextQuery factory to simplifyBlazeJPAQueryinstantiation.DatePartdefines date parts for various date/time operationsExtension forFetchableFilterableWindowOveris the first part of a WindowFunction construction.Utility methods for creating JPQL.next expressionsJPQLNextOpsprovides JPQL.Next specific operators.Query interface for JPQL.Next queriesQuery factory to simplifyBlazeJPAQueryinstantiation.Slightly adjustedJPQLSerializerimplementations that has basic support for rendering set operations.JPQLNextTemplatesextendsJPQLTemplatesto provide operator patterns for JPQL.Next serializationA named window.Visitor implementation that checks if a query is empty (i.e. has no default joins).SetExpression<RT>Set expresion defines an interface for set operation queries.SetExpressionImpl<T,Q extends AbstractBlazeJPAQuery<T, Q>> Default implementation forSetExpression.A special type ofQueryFlagthat indicates that a subquery represents a set operation.Utility methods for generating set operations.An expression type that represents aVALUESclause that can be used asFROMexpression.A base class for window definition expressions.WindowFunctionis a builder for window function expressions.WindowOver<T>WindowOveris the first part of a WindowFunction construction.WindowRowsprovides the building of the rows/range part of the window function expression.WithBuilder<R>WithBuilderis a builder for common table expressions.