-
-
Notifications
You must be signed in to change notification settings - Fork 76
#1528: Hibernate CTE support #1529
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,16 +17,19 @@ | |
| import com.querydsl.core.DefaultQueryMetadata; | ||
| import com.querydsl.core.NonUniqueResultException; | ||
| import com.querydsl.core.QueryException; | ||
| import com.querydsl.core.QueryFlag; | ||
| import com.querydsl.core.QueryMetadata; | ||
| import com.querydsl.core.QueryModifiers; | ||
| import com.querydsl.core.QueryResults; | ||
| import com.querydsl.core.types.ConstantImpl; | ||
| import com.querydsl.core.types.Expression; | ||
| import com.querydsl.core.types.ExpressionUtils; | ||
| import com.querydsl.core.types.FactoryExpression; | ||
| import com.querydsl.core.types.Path; | ||
| import com.querydsl.core.types.SubQueryExpression; | ||
| import com.querydsl.jpa.FactoryExpressionTransformer; | ||
| import com.querydsl.jpa.HQLTemplates; | ||
| import com.querydsl.jpa.JPAQueryBase; | ||
| import com.querydsl.jpa.JPQLSerializer; | ||
| import com.querydsl.jpa.JPQLTemplates; | ||
| import com.querydsl.jpa.ScrollableResultsIterator; | ||
| import java.util.HashMap; | ||
|
|
@@ -95,6 +98,29 @@ public long fetchCount() { | |
| } | ||
| } | ||
|
|
||
| public Q with(Path<?> alias, SubQueryExpression<?> query) { | ||
| return with(alias, null, query); | ||
| } | ||
|
Comment on lines
+101
to
+103
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hibernate only supports subqueries here, so there is no |
||
|
|
||
| public Q withMaterializedHint(Path<?> alias, SubQueryExpression<?> query) { | ||
| return with(alias, true, query); | ||
| } | ||
|
|
||
| public Q withNotMaterializedHint(Path<?> alias, SubQueryExpression<?> query) { | ||
| return with(alias, false, query); | ||
| } | ||
|
|
||
| public Q with(Path<?> alias, Boolean materialized, SubQueryExpression<?> query) { | ||
| Expression<?> expr = | ||
| ExpressionUtils.operation( | ||
| alias.getType(), | ||
| HQLOps.WITH, | ||
| alias, | ||
| materialized != null ? ConstantImpl.create(materialized) : null, | ||
| query); | ||
| return queryMixin.addFlag(new QueryFlag(QueryFlag.Position.WITH, expr)); | ||
| } | ||
|
|
||
| /** | ||
| * Expose the original Hibernate query for the given projection | ||
| * | ||
|
|
@@ -348,8 +374,8 @@ public T fetchOne() throws NonUniqueResultException { | |
| } | ||
|
|
||
| @Override | ||
| protected JPQLSerializer createSerializer() { | ||
| return new JPQLSerializer(getTemplates()); | ||
| protected HQLSerializer createSerializer() { | ||
| return new HQLSerializer(getTemplates()); | ||
| } | ||
|
|
||
| protected void clone(Q query) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| package com.querydsl.jpa.hibernate; | ||
|
|
||
| import com.querydsl.core.types.Operator; | ||
|
|
||
| public enum HQLOps implements Operator { | ||
| WITH(Object.class); | ||
|
|
||
| private final Class<?> type; | ||
|
|
||
| HQLOps(Class<?> type) { | ||
| this.type = type; | ||
| } | ||
|
|
||
| @Override | ||
| public Class<?> getType() { | ||
| return type; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| package com.querydsl.jpa.hibernate; | ||
|
|
||
| import com.querydsl.core.JoinExpression; | ||
| import com.querydsl.core.QueryFlag; | ||
| import com.querydsl.core.QueryMetadata; | ||
| import com.querydsl.core.types.ConstantImpl; | ||
| import com.querydsl.core.types.Expression; | ||
| import com.querydsl.core.types.Operator; | ||
| import com.querydsl.core.types.Path; | ||
| import com.querydsl.jpa.JPQLSerializer; | ||
| import com.querydsl.jpa.JPQLTemplates; | ||
| import jakarta.persistence.EntityManager; | ||
| import java.util.ArrayList; | ||
| import java.util.HashSet; | ||
| import java.util.List; | ||
| import java.util.Set; | ||
| import org.jetbrains.annotations.Nullable; | ||
|
|
||
| public class HQLSerializer extends JPQLSerializer { | ||
|
|
||
| protected final Set<Path<?>> withAliases = new HashSet<>(); | ||
|
|
||
| public HQLSerializer(JPQLTemplates templates) { | ||
| super(templates); | ||
| } | ||
|
|
||
| public HQLSerializer(JPQLTemplates templates, EntityManager em) { | ||
| super(templates, em); | ||
| } | ||
|
|
||
| @Override | ||
| public void serialize(QueryMetadata metadata, boolean forCountRow, @Nullable String projection) { | ||
| final Set<QueryFlag> flags = metadata.getFlags(); | ||
| final var hasFlags = !flags.isEmpty(); | ||
|
|
||
| if (hasFlags) { | ||
| List<Expression<?>> withFlags = new ArrayList<>(); | ||
| for (QueryFlag flag : flags) { | ||
| if (flag.getPosition() == QueryFlag.Position.WITH) { | ||
| withFlags.add(flag.getFlag()); | ||
| } | ||
| } | ||
| if (!withFlags.isEmpty()) { | ||
| append("with\n"); | ||
| handle(",\n", withFlags); | ||
| append("\n"); | ||
| } | ||
| } | ||
|
|
||
| super.serialize(metadata, forCountRow, projection); | ||
| } | ||
|
|
||
| @Override | ||
| protected void visitOperation( | ||
| Class<?> type, Operator operator, List<? extends Expression<?>> args) { | ||
| if (operator == HQLOps.WITH && args.size() == 3 && args.get(0) instanceof Path<?> alias) { | ||
| handle(alias); | ||
| withAliases.add(alias); | ||
| append(" as "); | ||
| if (args.get(1) instanceof ConstantImpl<?> materializedParam | ||
| && materializedParam.getConstant() instanceof Boolean materialized) { | ||
| if (!materialized) { | ||
| append("not "); | ||
| } | ||
| append("materialized "); | ||
| } | ||
| handle(args.get(2)); | ||
|
Comment on lines
+57
to
+67
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Originally I wanted to use templates, but I couldn't figure out how to get the |
||
| } else { | ||
| super.visitOperation(type, operator, args); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| protected void handleJoinTarget(JoinExpression je) { | ||
| if (je.getTarget() instanceof Path<?> pe && withAliases.contains(pe)) { | ||
| append(pe.getMetadata().getName()).append(" "); | ||
| handle(je.getTarget()); | ||
| } else { | ||
| super.handleJoinTarget(je); | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I couldn't find a nicer way to extend the logic of this method.
As
NativeSQLSerializeralso has a similarly named method, which is also marked asprotectedI hope this is okay to do.