This is an automated email from the ASF dual-hosted git repository.
ntimofeev pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git
The following commit(s) were added to refs/heads/master by this push:
new 55acf42bc CAY-2844 Joint prefetch doesn't use ObjEntity qualifier
55acf42bc is described below
commit 55acf42bcd29f3d663f5e343d20e8f681c856c43
Author: Nikita Timofeev <[email protected]>
AuthorDate: Tue Mar 5 13:26:51 2024 +0400
CAY-2844 Joint prefetch doesn't use ObjEntity qualifier
---
RELEASE-NOTES.txt | 3 +-
.../access/translator/select/PathProcessor.java | 4 +-
.../translator/select/PrefetchNodeStage.java | 18 +++++-
.../access/translator/select/TableTree.java | 11 +++-
.../access/translator/select/TableTreeNode.java | 11 +++-
.../translator/select/TableTreeQualifierStage.java | 41 ++++++------
.../translator/select/TranslatorContext.java | 11 ++++
.../org/apache/cayenne/CDOQualifiedEntitiesIT.java | 72 ++++++++++++++++++++++
8 files changed, 139 insertions(+), 32 deletions(-)
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 0fdc8e180..1b60260c1 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -98,4 +98,5 @@ CAY-2815 Incorrect translation of aliased expression
CAY-2827 Saved data-source XML data doesn't correspond to the XSD schema
CAY-2838 Vertical Inheritance: Problem setting db attribute to null via
flattened path
CAY-2840 Vertical Inheritance: Missing subclass attributes with joint prefetch
-CAY-2841 Multi column ColumnSelect with SHARED_CACHE fails after 1st select
\ No newline at end of file
+CAY-2841 Multi column ColumnSelect with SHARED_CACHE fails after 1st select
+CAY-2844 Joint prefetch doesn't use ObjEntity qualifier
\ No newline at end of file
diff --git
a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/PathProcessor.java
b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/PathProcessor.java
index 2f4712cbe..f4968031b 100644
---
a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/PathProcessor.java
+++
b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/PathProcessor.java
@@ -36,7 +36,6 @@ import java.util.Optional;
*/
abstract class PathProcessor<T extends Entity<?,?,?>> implements
PathTranslationResult {
- public static final char OUTER_JOIN_INDICATOR = '+';
public static final char SPLIT_PATH_INDICATOR = '#';
public static final String DB_PATH_ALIAS_INDICATOR = "db:";
@@ -62,6 +61,9 @@ abstract class PathProcessor<T extends Entity<?,?,?>>
implements PathTranslation
}
public PathTranslationResult process(CayennePath path) {
+ if(path.marker() != CayennePath.NO_MARKER) {
+ currentDbPath = currentDbPath.withMarker(path.marker());
+ }
List<CayennePathSegment> segments = path.segments();
int size = segments.size();
for (int i = 0; i < size; i++) {
diff --git
a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/PrefetchNodeStage.java
b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/PrefetchNodeStage.java
index e4d010c71..accabd374 100644
---
a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/PrefetchNodeStage.java
+++
b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/PrefetchNodeStage.java
@@ -81,9 +81,11 @@ class PrefetchNodeStage implements TranslationStage {
for(PrefetchTreeNode node : prefetch.adjacentJointNodes()) {
Expression prefetchExp = ExpressionFactory.pathExp(node.getPath());
+ ObjRelationship targetRel = (ObjRelationship)
prefetchExp.evaluate(objEntity);
ASTDbPath dbPrefetch = (ASTDbPath)
objEntity.translateToDbPath(prefetchExp);
CayennePath dbPath = dbPrefetch.getPath();
DbEntity dbEntity = objEntity.getDbEntity();
+ Expression targetQualifier =
context.getResolver().getClassDescriptor(targetRel.getTargetEntityName()).getEntityInheritanceTree().qualifierForEntityAndSubclasses();
CayennePath fullPath =
CayennePath.EMPTY_PATH.withMarker(CayennePath.PREFETCH_MARKER);
@@ -94,13 +96,16 @@ class PrefetchNodeStage implements TranslationStage {
}
fullPath = fullPath.dot(c);
- context.getTableTree().addJoinTable(fullPath, rel,
JoinType.LEFT_OUTER);
+ if(targetQualifier != null && c == dbPath.last()) {
+ targetQualifier =
translateToPrefetchQualifier(targetRel.getTargetEntity(), targetQualifier);
+
context.getTableTree().addJoinTable(fullPath.withMarker(CayennePath.PREFETCH_MARKER),
rel, JoinType.LEFT_OUTER, targetQualifier);
+ } else {
+
context.getTableTree().addJoinTable(fullPath.withMarker(CayennePath.PREFETCH_MARKER),
rel, JoinType.LEFT_OUTER);
+ }
dbEntity = rel.getTargetEntity();
}
- ObjRelationship targetRel = (ObjRelationship)
prefetchExp.evaluate(objEntity);
ClassDescriptor prefetchClassDescriptor =
context.getResolver().getClassDescriptor(targetRel.getTargetEntityName());
-
DescriptorColumnExtractor columnExtractor = new
DescriptorColumnExtractor(context, prefetchClassDescriptor);
columnExtractor.extract(dbPath.withMarker(CayennePath.PREFETCH_MARKER));
@@ -117,6 +122,13 @@ class PrefetchNodeStage implements TranslationStage {
}
}
+ Expression translateToPrefetchQualifier(ObjEntity entity, Expression
targetQualifier) {
+ Expression expression = entity.translateToDbPath(targetQualifier);
+ return expression.transform(o -> o instanceof ASTDbPath
+ ? ExpressionFactory.dbPathExp(((ASTDbPath)
o).getPath().withMarker(CayennePath.PREFETCH_MARKER))
+ : o);
+ }
+
private void processPrefetchQuery(TranslatorContext context) {
Select<?> select = context.getQuery().unwrap();
if(!(select instanceof PrefetchSelectQuery)) {
diff --git
a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTree.java
b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTree.java
index 43fbbda33..87ab9bcd0 100644
---
a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTree.java
+++
b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTree.java
@@ -25,11 +25,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.path.CayennePath;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.map.DbRelationship;
import org.apache.cayenne.map.JoinType;
-import org.apache.cayenne.util.Util;
/**
* @since 4.2
@@ -57,11 +57,16 @@ class TableTree {
}
void addJoinTable(CayennePath path, DbRelationship relationship, JoinType
joinType) {
- if (tableNodes.get(path) != null) {
+ addJoinTable(path, relationship, joinType, null);
+ }
+
+ void addJoinTable(CayennePath path, DbRelationship relationship, JoinType
joinType, Expression additionalQualifier) {
+ TableTreeNode treeNode = tableNodes.get(path);
+ if (treeNode != null) {
return;
}
- TableTreeNode node = new TableTreeNode(path, relationship,
nextTableAlias(), joinType);
+ TableTreeNode node = new TableTreeNode(path, relationship,
nextTableAlias(), joinType, additionalQualifier);
tableNodes.put(path, node);
}
diff --git
a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTreeNode.java
b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTreeNode.java
index b4a09117a..c2b68563b 100644
---
a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTreeNode.java
+++
b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTreeNode.java
@@ -19,6 +19,7 @@
package org.apache.cayenne.access.translator.select;
+import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.path.CayennePath;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.map.DbRelationship;
@@ -38,20 +39,24 @@ class TableTreeNode {
// relationship that connects this node with parent (or null if this is
root)
private final DbRelationship relationship;
+ private final Expression additionalQualifier;
+
TableTreeNode(DbEntity entity, String tableAlias) {
this.attributePath = CayennePath.EMPTY_PATH;
this.entity = entity;
this.tableAlias = tableAlias;
this.relationship = null;
this.joinType = null;
+ this.additionalQualifier = null;
}
- TableTreeNode(CayennePath path, DbRelationship relationship, String
tableAlias, JoinType joinType) {
+ TableTreeNode(CayennePath path, DbRelationship relationship, String
tableAlias, JoinType joinType, Expression additionalQualifier) {
this.attributePath = path;
this.entity = relationship.getTargetEntity();
this.tableAlias = tableAlias;
this.relationship = relationship;
this.joinType = joinType;
+ this.additionalQualifier = additionalQualifier;
}
public CayennePath getAttributePath() {
@@ -73,4 +78,8 @@ class TableTreeNode {
public DbRelationship getRelationship() {
return relationship;
}
+
+ public Expression getAdditionalQualifier() {
+ return additionalQualifier;
+ }
}
diff --git
a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTreeQualifierStage.java
b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTreeQualifierStage.java
index ba7eaec7b..313582ca8 100644
---
a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTreeQualifierStage.java
+++
b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTreeQualifierStage.java
@@ -19,16 +19,12 @@
package org.apache.cayenne.access.translator.select;
-import org.apache.cayenne.access.sqlbuilder.NodeBuilder;
import org.apache.cayenne.access.sqlbuilder.sqltree.Node;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.parser.ASTDbPath;
import org.apache.cayenne.exp.parser.ASTPath;
import org.apache.cayenne.exp.path.CayennePath;
-import static org.apache.cayenne.access.sqlbuilder.SQLBuilder.exp;
-import static org.apache.cayenne.access.sqlbuilder.SQLBuilder.node;
-
/**
* @since 4.2
*/
@@ -37,29 +33,28 @@ class TableTreeQualifierStage implements TranslationStage {
@Override
public void perform(TranslatorContext context) {
context.getTableTree().visit(node -> {
- Expression dbQualifier = node.getEntity().getQualifier();
- if (dbQualifier != null) {
- CayennePath pathToRoot = node.getAttributePath();
- dbQualifier = dbQualifier.transform(input -> {
- if (input instanceof ASTPath) {
- CayennePath path = pathToRoot.dot(((ASTPath)
input).getPath());
- return new ASTDbPath(path);
- }
- return input;
- });
- Node rootQualifier = context.getQualifierNode();
- Node translatedQualifier =
context.getQualifierTranslator().translate(dbQualifier);
- if (rootQualifier != null) {
- NodeBuilder expressionNodeBuilder =
exp(node(rootQualifier)).and(node(translatedQualifier));
- context.setQualifierNode(expressionNodeBuilder.build());
- } else {
- context.setQualifierNode(translatedQualifier);
- }
- }
+ appendQualifier(context, node, node.getEntity().getQualifier());
+ appendQualifier(context, node, node.getAdditionalQualifier());
});
if(context.getQualifierNode() != null) {
context.getSelectBuilder().where(context.getQualifierNode());
}
}
+
+ private static void appendQualifier(TranslatorContext context,
TableTreeNode node, Expression dbQualifier) {
+ if (dbQualifier == null) {
+ return;
+ }
+
+ CayennePath pathToRoot = node.getAttributePath();
+ dbQualifier = dbQualifier.transform(input ->
+ // here we are not only marking path as prefetch, but changing
ObjPath to DB (without )
+ input instanceof ASTPath
+ ? new ASTDbPath(pathToRoot.dot(((ASTPath)
input).getPath()).withMarker(CayennePath.PREFETCH_MARKER))
+ : input
+ );
+ Node translatedQualifier =
context.getQualifierTranslator().translate(dbQualifier);
+ context.appendQualifierNode(translatedQualifier);
+ }
}
diff --git
a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TranslatorContext.java
b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TranslatorContext.java
index 0c9894549..237cfac07 100644
---
a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TranslatorContext.java
+++
b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TranslatorContext.java
@@ -42,6 +42,9 @@ import org.apache.cayenne.map.SQLResult;
import org.apache.cayenne.query.Ordering;
import org.apache.cayenne.query.QueryMetadata;
+import static org.apache.cayenne.access.sqlbuilder.SQLBuilder.exp;
+import static org.apache.cayenne.access.sqlbuilder.SQLBuilder.node;
+
/**
* Context that holds all data necessary for query translation as well as a
result of that translation.
*
@@ -291,6 +294,14 @@ public class TranslatorContext implements
SQLGenerationContext {
this.qualifierNode = qualifierNode;
}
+ void appendQualifierNode(Node qualifierNode) {
+ if(this.qualifierNode == null) {
+ this.qualifierNode = qualifierNode;
+ } else {
+ this.qualifierNode =
exp(node(this.qualifierNode)).and(node(qualifierNode)).build();
+ }
+ }
+
Node getQualifierNode() {
return qualifierNode;
}
diff --git
a/cayenne/src/test/java/org/apache/cayenne/CDOQualifiedEntitiesIT.java
b/cayenne/src/test/java/org/apache/cayenne/CDOQualifiedEntitiesIT.java
index e5bffea2d..68b722774 100644
--- a/cayenne/src/test/java/org/apache/cayenne/CDOQualifiedEntitiesIT.java
+++ b/cayenne/src/test/java/org/apache/cayenne/CDOQualifiedEntitiesIT.java
@@ -128,6 +128,78 @@ public class CDOQualifiedEntitiesIT extends RuntimeCase {
}
}
+ @Test
+ public void testJointPrefetchToMany() throws Exception {
+ if (accessStackAdapter.supportsNullBoolean()) {
+
+ createReadToManyDataSet();
+
+ List<Qualified1> roots = ObjectSelect.query(Qualified1.class)
+ .prefetch(Qualified1.QUALIFIED2S.joint())
+ .select(context);
+
+ assertEquals(1, roots.size());
+
+ Qualified1 root = roots.get(0);
+
+ assertEquals("OX1", root.getName());
+
+ List<Qualified2> related = root.getQualified2s();
+ assertEquals(1, related.size());
+
+ Qualified2 r = related.get(0);
+ assertEquals("OY1", r.getName());
+ }
+ }
+
+ @Test
+ public void testDisjointPrefetchToMany() throws Exception {
+ if (accessStackAdapter.supportsNullBoolean()) {
+
+ createReadToManyDataSet();
+
+ List<Qualified1> roots = ObjectSelect.query(Qualified1.class)
+ .prefetch(Qualified1.QUALIFIED2S.disjoint())
+ .select(context);
+
+ assertEquals(1, roots.size());
+
+ Qualified1 root = roots.get(0);
+
+ assertEquals("OX1", root.getName());
+
+ List<Qualified2> related = root.getQualified2s();
+ assertEquals(1, related.size());
+
+ Qualified2 r = related.get(0);
+ assertEquals("OY1", r.getName());
+ }
+ }
+
+ @Test
+ public void testDisjointByIdPrefetchToMany() throws Exception {
+ if (accessStackAdapter.supportsNullBoolean()) {
+
+ createReadToManyDataSet();
+
+ List<Qualified1> roots = ObjectSelect.query(Qualified1.class)
+ .prefetch(Qualified1.QUALIFIED2S.disjointById())
+ .select(context);
+
+ assertEquals(1, roots.size());
+
+ Qualified1 root = roots.get(0);
+
+ assertEquals("OX1", root.getName());
+
+ List<Qualified2> related = root.getQualified2s();
+ assertEquals(1, related.size());
+
+ Qualified2 r = related.get(0);
+ assertEquals("OY1", r.getName());
+ }
+ }
+
@Test
public void testReadToOne() throws Exception {
if (accessStackAdapter.supportsNullBoolean()) {