mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-10-21 08:56:22 +00:00
Fix OrmQueryProperties.filterManyHasNestedProperty() with real nested property check
This commit is contained in:
@@ -12,7 +12,7 @@ public final class SpiExpressionValidation {
|
||||
|
||||
private final BeanType<?> desc;
|
||||
private final LinkedHashSet<String> unknown = new LinkedHashSet<>();
|
||||
private boolean nestedProperty;
|
||||
private final LinkedHashSet<String> all = new LinkedHashSet<>();
|
||||
|
||||
public SpiExpressionValidation(BeanType<?> desc) {
|
||||
this.desc = desc;
|
||||
@@ -22,9 +22,7 @@ public final class SpiExpressionValidation {
|
||||
* Validate that the property expression (path) is valid.
|
||||
*/
|
||||
public void validate(String propertyName) {
|
||||
if (!nestedProperty && propertyName.indexOf('.') > -1) {
|
||||
nestedProperty = true;
|
||||
}
|
||||
all.add(propertyName);
|
||||
if (!desc.isValidExpression(propertyName)) {
|
||||
unknown.add(propertyName);
|
||||
}
|
||||
@@ -38,12 +36,13 @@ public final class SpiExpressionValidation {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if any property visited during this validation referenced a nested/associated
|
||||
* path (i.e. contained a '.'). Used to inspect the shape of an expression without needing a
|
||||
* correctly-typed bean descriptor.
|
||||
* Return the set of all property names visited during this validation, regardless of
|
||||
* whether they were considered valid against the bean type. Used to inspect the shape of
|
||||
* an expression (for example, to check whether it references any associated/joined path)
|
||||
* without needing a correctly-typed bean descriptor.
|
||||
*/
|
||||
public boolean hasNestedProperty() {
|
||||
return nestedProperty;
|
||||
public Set<String> allProperties() {
|
||||
return all;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import io.ebean.FetchConfig;
|
||||
import io.ebean.event.BeanQueryRequest;
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.api.SpiExpressionList;
|
||||
import io.ebeaninternal.api.SpiExpressionValidation;
|
||||
import io.ebeaninternal.api.SpiQueryManyJoin;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssoc;
|
||||
@@ -385,7 +384,8 @@ public final class OrmQueryDetail implements Serializable {
|
||||
OrmQueryProperties chunk = pair.getProperties();
|
||||
if (isQueryJoinCandidate(lazyLoadManyPath, chunk)) {
|
||||
// this is a 'fetch join' (included in main query)
|
||||
if (fetchJoinFirstMany && !chunk.filterManyHasNestedProperty(new SpiExpressionValidation(beanDescriptor))) {
|
||||
BeanDescriptor<?> targetDescriptor = ((BeanPropertyAssoc<?>) elProp.beanProperty()).targetDescriptor();
|
||||
if (fetchJoinFirstMany && !chunk.filterManyHasNestedProperty(targetDescriptor)) {
|
||||
// letting the first one remain a 'fetch join'
|
||||
fetchJoinFirstMany = false;
|
||||
manyFetchProperty = pair.getPath();
|
||||
@@ -393,8 +393,9 @@ public final class OrmQueryDetail implements Serializable {
|
||||
many = elProp;
|
||||
} else {
|
||||
// convert this one over to a 'query join' - either because another many has already claimed the
|
||||
// 'fetch join' slot, or because its filterMany references a nested property that can't safely be
|
||||
// included as a JOIN predicate (see OrmQueryProperties.filterManyHasNestedProperty)
|
||||
// 'fetch join' slot, or because its filterMany references a property that requires crossing into
|
||||
// an associated bean and can't safely be included as a JOIN predicate (see
|
||||
// OrmQueryProperties.filterManyHasNestedProperty)
|
||||
chunk.markForQueryJoin();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ import io.ebeaninternal.api.SpiExpressionFactory;
|
||||
import io.ebeaninternal.api.SpiExpressionList;
|
||||
import io.ebeaninternal.api.SpiExpressionValidation;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebeaninternal.server.expression.FilterExprPath;
|
||||
import io.ebeaninternal.server.expression.FilterExpressionList;
|
||||
|
||||
@@ -236,16 +238,23 @@ public final class OrmQueryProperties implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the filterMany expression (if any) references a nested/associated
|
||||
* property - a dotted path such as {@code "group.name"} - rather than only direct
|
||||
* properties of the many bean itself.
|
||||
* Return true if the filterMany expression (if any) references a property that requires
|
||||
* crossing into an associated bean/join - e.g. {@code "group.name"} - rather than only
|
||||
* plain/embedded properties resolving to columns on the many bean's own base table.
|
||||
*/
|
||||
boolean filterManyHasNestedProperty(SpiExpressionValidation validation) {
|
||||
boolean filterManyHasNestedProperty(BeanDescriptor<?> targetDescriptor) {
|
||||
if (filterMany == null) {
|
||||
return false;
|
||||
}
|
||||
SpiExpressionValidation validation = new SpiExpressionValidation(targetDescriptor);
|
||||
filterMany.validate(validation);
|
||||
return validation.hasNestedProperty();
|
||||
for (String property : validation.allProperties()) {
|
||||
ElPropertyValue elProp = targetDescriptor.elGetValue(property);
|
||||
if (elProp != null && elProp.isAssocProperty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user