From f4ef6d33c218805c501d0e25aa8f3440712a11f6 Mon Sep 17 00:00:00 2001 From: Iliya Ivanov Date: Wed, 3 Dec 2025 20:00:55 +0200 Subject: [PATCH 1/6] Make embeddable diggable --- .../server/deploy/BeanDescriptor.java | 10 ++-- .../server/deploy/BeanPropertyAssoc.java | 6 --- .../server/deploy/BeanPropertyAssocOne.java | 16 +++---- .../server/el/ElPropertyChain.java | 26 ++++------ .../server/el/ElPropertyChainBuilder.java | 22 +++------ .../server/query/SqlTreeAlias.java | 47 ++++++++++--------- .../server/query/SqlTreeBuilder.java | 8 +++- .../query/SqlTreeNodeManyWhereJoin.java | 3 ++ 8 files changed, 66 insertions(+), 72 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java index 6b580119ff..de1513a103 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java @@ -2362,6 +2362,12 @@ ElPropertyValue buildElGetValue(String propName, ElPropertyChainBuilder chain, b if (assocProp == null) { return null; } + // this method is an entry-point, although it introduces recursive calls via + // buildElPropertyValue -> createElPropertyValue -> buildElGetValue (back to here) + // it seams we can initialize ElPropertyChainBuilder at this point and skip further checks. + if (chain == null) { + chain = new ElPropertyChainBuilder(propName); + } String remainder = propName.substring(basePos + 1); return assocProp.buildElPropertyValue(propName, remainder, chain, propertyDeploy); } @@ -2373,9 +2379,7 @@ ElPropertyValue buildElGetValue(String propName, ElPropertyChainBuilder chain, b if (property == null) { throw new PersistenceException("No property found for [" + propName + "] in expression " + chain.expression()); } - if (property.containsMany()) { - chain.setContainsMany(); - } + return chain.add(property).build(); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssoc.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssoc.java index ef10e0dbc6..966b2e3094 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssoc.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssoc.java @@ -161,13 +161,7 @@ public boolean hasForeignKeyIndex() { ElPropertyValue createElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) { // associated or embedded bean BeanDescriptor embDesc = targetDescriptor(); - if (chain == null) { - chain = new ElPropertyChainBuilder(isEmbedded(), propName); - } chain.add(this); - if (containsMany()) { - chain.setContainsMany(); - } return embDesc.buildElGetValue(remainder, chain, propertyDeploy); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java index 5bb4fe2c02..544a31946c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java @@ -210,17 +210,17 @@ Object naturalKeyVal(Map values) { @Override public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) { if (embedded) { - BeanProperty embProp = embeddedPropsMap.get(remainder); + String embName = remainder; + int basePos = remainder.indexOf('.'); + if (basePos > -1) { + embName = remainder.substring(0, basePos); + } + BeanProperty embProp = embeddedPropsMap.get(embName); + if (embProp == null) { - String msg = "Embedded Property " + remainder + " not found in " + fullName(); + String msg = "Embedded Property " + embName + " not found in " + fullName(); throw new PersistenceException(msg); } - if (chain == null) { - chain = new ElPropertyChainBuilder(true, propName); - } - chain.add(this); - chain.setEmbedded(true); - return chain.add(embProp).build(); } return createElPropertyValue(propName, remainder, chain, propertyDeploy); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/el/ElPropertyChain.java b/ebean-core/src/main/java/io/ebeaninternal/server/el/ElPropertyChain.java index 12d376bed0..f023764346 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/el/ElPropertyChain.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/el/ElPropertyChain.java @@ -37,38 +37,32 @@ public final class ElPropertyChain implements ElPropertyValue { private final ScalarType scalarType; private final ElPropertyValue lastElPropertyValue; - public ElPropertyChain(boolean containsMany, boolean embedded, String expression, ElPropertyValue[] chain) { - this.containsMany = containsMany; + public ElPropertyChain(String expression, boolean containsMany, ElPropertyValue chain[]) { this.chain = chain; this.expression = expression; + this.containsMany = containsMany; + int dotPos = expression.lastIndexOf('.'); if (dotPos > -1) { this.name = expression.substring(dotPos + 1); - if (embedded) { - int embPos = expression.lastIndexOf('.', dotPos - 1); - this.prefix = embPos == -1 ? null : expression.substring(0, embPos); - - } else { - this.prefix = expression.substring(0, dotPos); - } + this.prefix = expression.substring(0, dotPos); } else { this.prefix = null; this.name = expression; } - this.assocId = chain[chain.length - 1].isAssocId(); - - this.last = chain.length - 1; - this.lastBeanProperty = chain[chain.length - 1].beanProperty(); + this.last = this.chain.length - 1; + this.lastElPropertyValue = this.chain[this.last]; + this.assocId = this.lastElPropertyValue.isAssocId(); + this.lastBeanProperty = lastElPropertyValue.beanProperty(); if (lastBeanProperty != null) { this.scalarType = lastBeanProperty.scalarType(); } else { // case for nested compound type (non-scalar) this.scalarType = null; } - this.lastElPropertyValue = chain[chain.length - 1]; - this.placeHolder = placeHolder(prefix, lastElPropertyValue, false); - this.placeHolderEncrypted = placeHolder(prefix, lastElPropertyValue, true); + this.placeHolder = placeHolder(this.prefix, lastElPropertyValue, false); + this.placeHolderEncrypted = placeHolder(this.prefix, lastElPropertyValue, true); } @Override diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/el/ElPropertyChainBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/el/ElPropertyChainBuilder.java index 409f17bc70..d372330333 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/el/ElPropertyChainBuilder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/el/ElPropertyChainBuilder.java @@ -17,14 +17,12 @@ public final class ElPropertyChainBuilder { private final String expression; private final List chain = new ArrayList<>(); - private boolean embedded; - private boolean containsMany; + private boolean containsMany = false; /** * Create with the original expression. */ - public ElPropertyChainBuilder(boolean embedded, String expression) { - this.embedded = embedded; + public ElPropertyChainBuilder(String expression) { this.expression = expression; } @@ -32,10 +30,6 @@ public boolean isContainsMany() { return containsMany; } - public void setContainsMany() { - this.containsMany = true; - } - public String expression() { return expression; } @@ -48,6 +42,9 @@ public ElPropertyChainBuilder add(ElPropertyValue element) { throw new NullPointerException("element null in expression " + expression); } chain.add(element); + if (element.containsMany()) { + containsMany = true; + } return this; } @@ -55,13 +52,6 @@ public ElPropertyChainBuilder add(ElPropertyValue element) { * Build the immutable ElGetChain from the build information. */ public ElPropertyChain build() { - return new ElPropertyChain(containsMany, embedded, expression, chain.toArray(new ElPropertyValue[0])); - } - - /** - * Permits to set whole chain as embedded when the leaf is embedded - */ - public void setEmbedded(boolean embedded) { - this.embedded = embedded; + return new ElPropertyChain(expression, containsMany, chain.toArray(new ElPropertyValue[0])); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeAlias.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeAlias.java index 3a5e7bdc7d..06f1f4a090 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeAlias.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeAlias.java @@ -4,7 +4,6 @@ import io.ebeaninternal.api.SpiQuery; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.TreeSet; @@ -16,7 +15,8 @@ final class SqlTreeAlias { private final SpiQuery.TemporalMode temporalMode; private final TreeSet joinProps = new TreeSet<>(); - private HashSet embeddedPropertyJoins; + // embedded property as key, and true if many-where-property + private HashMap embeddedPropertyJoins; private final TreeSet manyWhereJoinProps = new TreeSet<>(); private final HashMap aliasMap = new HashMap<>(); private final HashMap manyWhereAliasMap = new HashMap<>(); @@ -32,19 +32,19 @@ final class SqlTreeAlias { /** * Add joins to support where predicates */ - void addManyWhereJoins(Set manyWhereJoins) { + void addManyWhereJoins(Set manyWhereJoins, STreeType desc) { if (manyWhereJoins != null) { for (String include : manyWhereJoins) { - addPropertyJoin(include, manyWhereJoinProps); + addPropertyJoin(include, manyWhereJoinProps, desc, true); } } } - private void addEmbeddedPropertyJoin(String embProp) { + private void addEmbeddedPropertyJoin(String embProp, Boolean isManyWhere) { if (embeddedPropertyJoins == null) { - embeddedPropertyJoins = new HashSet<>(); + embeddedPropertyJoins = new HashMap<>(); } - embeddedPropertyJoins.add(embProp); + embeddedPropertyJoins.put(embProp, isManyWhere); } /** @@ -53,21 +53,21 @@ private void addEmbeddedPropertyJoin(String embProp) { public void addJoin(Set propJoins, STreeType desc) { if (propJoins != null) { for (String propJoin : propJoins) { - if (desc.isEmbeddedPath(propJoin)) { - addEmbeddedPropertyJoin(propJoin); - } else { - addPropertyJoin(propJoin, joinProps); - } + addPropertyJoin(propJoin, joinProps, desc, false); } } } - private void addPropertyJoin(String include, TreeSet set) { - if (set.add(include)) { - String[] split = SplitName.split(include); - if (split[0] != null) { - addPropertyJoin(split[0], set); - } + private void addPropertyJoin(String include, TreeSet set, STreeType desc, Boolean isManyWhere) { + if (include == null) { + return; + } + String[] split = SplitName.split(include); + if (desc.isEmbeddedPath(include)) { + addEmbeddedPropertyJoin(include, isManyWhere); + addPropertyJoin(split[0], set, desc, isManyWhere); + } else if (set.add(include)) { + addPropertyJoin(split[0], set, desc, isManyWhere); } } @@ -86,11 +86,14 @@ void buildAlias() { private void mapEmbeddedPropertyAlias() { if (embeddedPropertyJoins != null) { - for (String propJoin : embeddedPropertyJoins) { - String[] split = SplitName.split(propJoin); + for (Map.Entry propJoin : embeddedPropertyJoins.entrySet()) { + String[] split = SplitName.split(propJoin.getKey()); // the table alias of the parent path - String alias = tableAlias(split[0]); - aliasMap.put(propJoin, alias); + if (Boolean.TRUE.equals(propJoin.getValue())) { + manyWhereAliasMap.put(propJoin.getKey(), tableAliasManyWhere(split[0])); + } else { + aliasMap.put(propJoin.getKey(), tableAlias(split[0])); + } } } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java index 9b828fa9db..90b84ce3a3 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java @@ -220,7 +220,7 @@ private SqlTreeNode buildRootNode(STreeType desc) { if (!rawSql) { alias.addJoin(queryDetail.getFetchPaths(), desc); alias.addJoin(predicates.predicateIncludes(), desc); - alias.addManyWhereJoins(manyWhereJoins.propertyNames()); + alias.addManyWhereJoins(manyWhereJoins.propertyNames(), desc); // build set of table alias alias.buildAlias(); predicates.parseTableAlias(alias); @@ -681,6 +681,12 @@ private SqlTreeNodeExtraJoin findExtraJoinRoot(String includeProp, SqlTreeNodeEx // parent already handled by select return childJoin; } + if (desc.isEmbeddedPath(parentPropertyName)) { + // digging in embedded property + // so we have to join parent property path + includeProp = parentPropertyName; + continue; + } SqlTreeNodeExtraJoin parentJoin = joinRegister.get(parentPropertyName); if (parentJoin == null) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java index ada05c4c97..052d71055e 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java @@ -83,6 +83,9 @@ public void appendFrom(DbSqlContext ctx, SqlJoinType currentJoinType) { * intersection table if this is a ManyToMany node. */ private void appendFromBaseTable(DbSqlContext ctx, SqlJoinType joinType) { + if (nodeBeanProp.isEmbedded()) { + return; + } String alias = ctx.tableAliasManyWhere(prefix); String parentAlias = ctx.tableAliasManyWhere(parentPrefix); From 994af7bcf1a75fa4e3414908237a7c7945fd5ec7 Mon Sep 17 00:00:00 2001 From: Iliya Ivanov Date: Sun, 7 Dec 2025 10:09:29 +0200 Subject: [PATCH 2/6] WIP --- .../ebeaninternal/server/query/SqlTreeBuilder.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java index 90b84ce3a3..984c13804f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java @@ -649,14 +649,19 @@ private void createExtraJoin(String includeProp) { * Create a SqlTreeNodeExtraJoin, register and return it. */ private SqlTreeNodeExtraJoin createJoinLeaf(String propertyName) { + if (propertyName == null) { + return null; + } + if (desc.isEmbeddedPath(propertyName)) { + return createJoinLeaf(SplitName.split(propertyName)[0]); + } ExtraJoin extra = desc.extraJoin(propertyName); if (extra == null) { return null; - } else { - SqlTreeNodeExtraJoin extraJoin = new SqlTreeNodeExtraJoin(propertyName, extra.property(), extra.isContainsMany(), temporalMode); - joinRegister.put(propertyName, extraJoin); - return extraJoin; } + SqlTreeNodeExtraJoin extraJoin = new SqlTreeNodeExtraJoin(propertyName, extra.property(), extra.isContainsMany(), temporalMode); + joinRegister.put(propertyName, extraJoin); + return extraJoin; } /** From 5fd6921c535d10f66d231ae1840611b0c433b3a3 Mon Sep 17 00:00:00 2001 From: Iliya Ivanov Date: Sun, 7 Dec 2025 10:17:29 +0200 Subject: [PATCH 3/6] WIP --- .../io/ebeaninternal/server/query/SqlTreeBuilder.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java index 984c13804f..735e6aa27c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java @@ -653,6 +653,8 @@ private SqlTreeNodeExtraJoin createJoinLeaf(String propertyName) { return null; } if (desc.isEmbeddedPath(propertyName)) { + // digging in embedded property + // so we have to join parent property path return createJoinLeaf(SplitName.split(propertyName)[0]); } ExtraJoin extra = desc.extraJoin(propertyName); @@ -686,12 +688,6 @@ private SqlTreeNodeExtraJoin findExtraJoinRoot(String includeProp, SqlTreeNodeEx // parent already handled by select return childJoin; } - if (desc.isEmbeddedPath(parentPropertyName)) { - // digging in embedded property - // so we have to join parent property path - includeProp = parentPropertyName; - continue; - } SqlTreeNodeExtraJoin parentJoin = joinRegister.get(parentPropertyName); if (parentJoin == null) { From 348aca1a4b7d6170a883c6bf07f21848637e0527 Mon Sep 17 00:00:00 2001 From: Iliya Ivanov Date: Sun, 7 Dec 2025 12:03:46 +0200 Subject: [PATCH 4/6] WIP --- .../ebeaninternal/server/deploy/BeanDescriptor.java | 4 +--- .../ebeaninternal/server/query/SqlTreeBuilder.java | 13 +++---------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java index de1513a103..b90db30163 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java @@ -2828,9 +2828,7 @@ public ExtraJoin extraJoin(String propertyPath) { BeanProperty beanProperty = elGetValue.beanProperty(); if (beanProperty instanceof BeanPropertyAssoc) { BeanPropertyAssoc assocProp = (BeanPropertyAssoc) beanProperty; - if (!assocProp.isEmbedded()) { - return new ExtraJoin(assocProp, elGetValue.containsMany()); - } + return new ExtraJoin(assocProp, elGetValue.containsMany()); } } return null; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java index 735e6aa27c..b6a3a6340a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java @@ -638,7 +638,7 @@ private void createExtraJoin(String includeProp) { // find root of this extra join... linking back to the // parents (creating the tree) as it goes. - SqlTreeNodeExtraJoin root = findExtraJoinRoot(includeProp, extraJoin); + SqlTreeNodeExtraJoin root = findExtraJoinRoot(extraJoin); // register the root because these are the only ones we // return back. rootRegister.put(root.name(), root); @@ -649,14 +649,6 @@ private void createExtraJoin(String includeProp) { * Create a SqlTreeNodeExtraJoin, register and return it. */ private SqlTreeNodeExtraJoin createJoinLeaf(String propertyName) { - if (propertyName == null) { - return null; - } - if (desc.isEmbeddedPath(propertyName)) { - // digging in embedded property - // so we have to join parent property path - return createJoinLeaf(SplitName.split(propertyName)[0]); - } ExtraJoin extra = desc.extraJoin(propertyName); if (extra == null) { return null; @@ -674,7 +666,8 @@ private SqlTreeNodeExtraJoin createJoinLeaf(String propertyName) { * not specified and is implicitly created. *

*/ - private SqlTreeNodeExtraJoin findExtraJoinRoot(String includeProp, SqlTreeNodeExtraJoin childJoin) { + private SqlTreeNodeExtraJoin findExtraJoinRoot(SqlTreeNodeExtraJoin childJoin) { + String includeProp = childJoin.name(); while (true) { int dotPos = includeProp.lastIndexOf('.'); if (dotPos == -1) { From 5c3212b0b27889ffe85da20533d063cd67cd0a1a Mon Sep 17 00:00:00 2001 From: Iliya Ivanov Date: Sun, 7 Dec 2025 14:33:34 +0200 Subject: [PATCH 5/6] WIP --- .../server/query/SqlTreeAlias.java | 52 +++++++++---------- .../server/query/SqlTreeBuilder.java | 12 ++--- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeAlias.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeAlias.java index 06f1f4a090..6a13a74e93 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeAlias.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeAlias.java @@ -4,6 +4,7 @@ import io.ebeaninternal.api.SpiQuery; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.TreeSet; @@ -15,8 +16,7 @@ final class SqlTreeAlias { private final SpiQuery.TemporalMode temporalMode; private final TreeSet joinProps = new TreeSet<>(); - // embedded property as key, and true if many-where-property - private HashMap embeddedPropertyJoins; + private HashSet embeddedPropertyJoins; private final TreeSet manyWhereJoinProps = new TreeSet<>(); private final HashMap aliasMap = new HashMap<>(); private final HashMap manyWhereAliasMap = new HashMap<>(); @@ -35,39 +35,42 @@ final class SqlTreeAlias { void addManyWhereJoins(Set manyWhereJoins, STreeType desc) { if (manyWhereJoins != null) { for (String include : manyWhereJoins) { - addPropertyJoin(include, manyWhereJoinProps, desc, true); + addPropertyJoin(include, manyWhereJoinProps, desc); } } } - private void addEmbeddedPropertyJoin(String embProp, Boolean isManyWhere) { + private boolean addEmbeddedPropertyJoin(String embProp) { if (embeddedPropertyJoins == null) { - embeddedPropertyJoins = new HashMap<>(); + embeddedPropertyJoins = new HashSet<>(); } - embeddedPropertyJoins.put(embProp, isManyWhere); + return embeddedPropertyJoins.add(embProp); } /** * Add joins. */ public void addJoin(Set propJoins, STreeType desc) { - if (propJoins != null) { - for (String propJoin : propJoins) { - addPropertyJoin(propJoin, joinProps, desc, false); - } + if (propJoins == null) { + return; + } + for (String propJoin : propJoins) { + addPropertyJoin(propJoin, joinProps, desc); } } - private void addPropertyJoin(String include, TreeSet set, STreeType desc, Boolean isManyWhere) { - if (include == null) { - return; - } - String[] split = SplitName.split(include); + private void addPropertyJoin(String include, TreeSet set, STreeType desc) { + boolean added = false; if (desc.isEmbeddedPath(include)) { - addEmbeddedPropertyJoin(include, isManyWhere); - addPropertyJoin(split[0], set, desc, isManyWhere); - } else if (set.add(include)) { - addPropertyJoin(split[0], set, desc, isManyWhere); + added = addEmbeddedPropertyJoin(include); + } else { + added = set.add(include); + } + if (added) { + String[] split = SplitName.split(include); + if (split[0] != null) { + addPropertyJoin(split[0], set, desc); + } } } @@ -86,14 +89,11 @@ void buildAlias() { private void mapEmbeddedPropertyAlias() { if (embeddedPropertyJoins != null) { - for (Map.Entry propJoin : embeddedPropertyJoins.entrySet()) { - String[] split = SplitName.split(propJoin.getKey()); + for (String propJoin : embeddedPropertyJoins) { + String[] split = SplitName.split(propJoin); // the table alias of the parent path - if (Boolean.TRUE.equals(propJoin.getValue())) { - manyWhereAliasMap.put(propJoin.getKey(), tableAliasManyWhere(split[0])); - } else { - aliasMap.put(propJoin.getKey(), tableAlias(split[0])); - } + String alias = tableAlias(split[0]); + aliasMap.put(propJoin, alias); } } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java index b6a3a6340a..7e52df477c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java @@ -638,7 +638,7 @@ private void createExtraJoin(String includeProp) { // find root of this extra join... linking back to the // parents (creating the tree) as it goes. - SqlTreeNodeExtraJoin root = findExtraJoinRoot(extraJoin); + SqlTreeNodeExtraJoin root = findExtraJoinRoot(includeProp, extraJoin); // register the root because these are the only ones we // return back. rootRegister.put(root.name(), root); @@ -652,10 +652,11 @@ private SqlTreeNodeExtraJoin createJoinLeaf(String propertyName) { ExtraJoin extra = desc.extraJoin(propertyName); if (extra == null) { return null; + } else { + SqlTreeNodeExtraJoin extraJoin = new SqlTreeNodeExtraJoin(propertyName, extra.property(), extra.isContainsMany(), temporalMode); + joinRegister.put(propertyName, extraJoin); + return extraJoin; } - SqlTreeNodeExtraJoin extraJoin = new SqlTreeNodeExtraJoin(propertyName, extra.property(), extra.isContainsMany(), temporalMode); - joinRegister.put(propertyName, extraJoin); - return extraJoin; } /** @@ -666,8 +667,7 @@ private SqlTreeNodeExtraJoin createJoinLeaf(String propertyName) { * not specified and is implicitly created. *

*/ - private SqlTreeNodeExtraJoin findExtraJoinRoot(SqlTreeNodeExtraJoin childJoin) { - String includeProp = childJoin.name(); + private SqlTreeNodeExtraJoin findExtraJoinRoot(String includeProp, SqlTreeNodeExtraJoin childJoin) { while (true) { int dotPos = includeProp.lastIndexOf('.'); if (dotPos == -1) { From 12a9f9897a4a3c2473933ec7daf336b2044b52da Mon Sep 17 00:00:00 2001 From: Iliya Ivanov Date: Sun, 7 Dec 2025 17:33:03 +0200 Subject: [PATCH 6/6] WIP --- .../server/query/SqlTreeAlias.java | 2 +- .../server/query/SqlTreeBuilder.java | 42 +++++++++++-------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeAlias.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeAlias.java index 6a13a74e93..fc6459e04e 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeAlias.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeAlias.java @@ -92,7 +92,7 @@ private void mapEmbeddedPropertyAlias() { for (String propJoin : embeddedPropertyJoins) { String[] split = SplitName.split(propJoin); // the table alias of the parent path - String alias = tableAlias(split[0]); + String alias = tableAliasManyWhere(split[0]); aliasMap.put(propJoin, alias); } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java index 7e52df477c..a5a55c4df4 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java @@ -362,7 +362,8 @@ private void buildExtraJoins(STreeType desc, List myList) { // the 'select' part of the query. We may need to add other joins to // support the predicates or order by clauses. - // remove ManyWhereJoins from the predicateIncludes + // remove ManyWhereJoins from the predicateIncludes and orderByIncludes + predicates.orderByIncludes().removeAll(manyWhereJoins.propertyNames()); predicateIncludes.removeAll(manyWhereJoins.propertyNames()); predicateIncludes.addAll(predicates.orderByIncludes()); @@ -649,11 +650,15 @@ private void createExtraJoin(String includeProp) { * Create a SqlTreeNodeExtraJoin, register and return it. */ private SqlTreeNodeExtraJoin createJoinLeaf(String propertyName) { + SqlTreeNodeExtraJoin extraJoin = joinRegister.get(propertyName); + if (extraJoin != null) { + return extraJoin; + } ExtraJoin extra = desc.extraJoin(propertyName); if (extra == null) { return null; } else { - SqlTreeNodeExtraJoin extraJoin = new SqlTreeNodeExtraJoin(propertyName, extra.property(), extra.isContainsMany(), temporalMode); + extraJoin = new SqlTreeNodeExtraJoin(propertyName, extra.property(), extra.isContainsMany(), temporalMode); joinRegister.put(propertyName, extraJoin); return extraJoin; } @@ -674,24 +679,25 @@ private SqlTreeNodeExtraJoin findExtraJoinRoot(String includeProp, SqlTreeNodeEx // no parent possible(parent is root) return childJoin; - } else { - // look in register ... - String parentPropertyName = includeProp.substring(0, dotPos); - if (selectIncludes.contains(parentPropertyName)) { - // parent already handled by select - return childJoin; - } - - SqlTreeNodeExtraJoin parentJoin = joinRegister.get(parentPropertyName); - if (parentJoin == null) { - // we need to create this the parent implicitly... - parentJoin = createJoinLeaf(parentPropertyName); - } - - parentJoin.addChild(childJoin); - childJoin = parentJoin; + } + String parentPropertyName = includeProp.substring(0, dotPos); + if (desc.isEmbeddedPath(parentPropertyName)) { + // digging in embedded property + // so we have to join parent property path if any includeProp = parentPropertyName; + continue; + } + // look in register ... + if (selectIncludes.contains(parentPropertyName)) { + // parent already handled by select + return childJoin; } + + SqlTreeNodeExtraJoin parentJoin = createJoinLeaf(parentPropertyName); + + parentJoin.addChild(childJoin); + childJoin = parentJoin; + includeProp = parentPropertyName; } }