Fix != and =out= for maps in G2 RSQL to Specification (#2426)
+ add initial draft of Standalone RSQL test + provide option to override Hibernate / Eclipselink configuration via standard spring environment properties Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -10,21 +10,12 @@
|
||||
package org.eclipse.hawkbit.repository.jpa.rsql;
|
||||
|
||||
import static org.eclipse.hawkbit.repository.rsql.RsqlConfigHolder.RsqlToSpecBuilder.G3;
|
||||
import static org.eclipse.hawkbit.repository.rsql.RsqlConfigHolder.RsqlToSpecBuilder.LEGACY_G1;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import jakarta.persistence.criteria.Root;
|
||||
|
||||
import cz.jirutka.rsql.parser.RSQLParser;
|
||||
import cz.jirutka.rsql.parser.RSQLParserException;
|
||||
import cz.jirutka.rsql.parser.ast.Node;
|
||||
import cz.jirutka.rsql.parser.ast.RSQLVisitor;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -32,15 +23,12 @@ import org.apache.commons.lang3.text.StrLookup;
|
||||
import org.eclipse.hawkbit.repository.RsqlQueryField;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||
import org.eclipse.hawkbit.repository.jpa.rsqllegacy.JpaQueryRsqlVisitor;
|
||||
import org.eclipse.hawkbit.repository.jpa.rsqllegacy.JpaQueryRsqlVisitorG2;
|
||||
import org.eclipse.hawkbit.repository.jpa.rsqllegacy.SpecificationBuilderLegacy;
|
||||
import org.eclipse.hawkbit.repository.rsql.RsqlConfigHolder;
|
||||
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
|
||||
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyResolver;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.orm.jpa.vendor.Database;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* A utility class which is able to parse RSQL strings into an spring data
|
||||
@@ -99,7 +87,11 @@ public final class RSQLUtility {
|
||||
return new SpecificationBuilder<T>(
|
||||
virtualPropertyReplacer,
|
||||
!RsqlConfigHolder.getInstance().isCaseInsensitiveDB() && RsqlConfigHolder.getInstance().isIgnoreCase(),
|
||||
database).specification(RsqlParser.parse(rsql, rsqlQueryFieldType));
|
||||
database)
|
||||
.specification(RsqlParser.parse(
|
||||
RsqlConfigHolder.getInstance().isCaseInsensitiveDB() || RsqlConfigHolder.getInstance().isIgnoreCase()
|
||||
? rsql.toLowerCase() : rsql,
|
||||
rsqlQueryFieldType));
|
||||
} else {
|
||||
return new SpecificationBuilderLegacy<A, T>(rsqlQueryFieldType, virtualPropertyReplacer, database).specification(rsql);
|
||||
}
|
||||
@@ -113,7 +105,7 @@ public final class RSQLUtility {
|
||||
* @throws RSQLParserException if RSQL syntax is invalid
|
||||
* @throws RSQLParameterUnsupportedFieldException if RSQL key is not allowed
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public static <A extends Enum<A> & RsqlQueryField> void validateRsqlFor(
|
||||
final String rsql, final Class<A> rsqlQueryFieldType,
|
||||
final Class<?> jpaType,
|
||||
@@ -121,6 +113,6 @@ public final class RSQLUtility {
|
||||
final CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
|
||||
final CriteriaQuery<?> criteriaQuery = criteriaBuilder.createQuery(jpaType);
|
||||
buildRsqlSpecification(rsql, rsqlQueryFieldType, virtualPropertyReplacer, null)
|
||||
.toPredicate(criteriaQuery.from((Class)jpaType), criteriaQuery, criteriaBuilder);
|
||||
.toPredicate(criteriaQuery.from((Class) jpaType), criteriaQuery, criteriaBuilder);
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,6 @@ import org.eclipse.hawkbit.repository.RsqlQueryField;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||
import org.eclipse.hawkbit.repository.jpa.rsql.Node.Comparison;
|
||||
import org.eclipse.hawkbit.repository.rsql.RsqlConfigHolder;
|
||||
|
||||
/**
|
||||
* {@link RsqlParser} parses RSQL query stings to {@link Node} objects. Doing that it does the following:
|
||||
@@ -82,15 +81,13 @@ public class RsqlParser {
|
||||
}
|
||||
|
||||
public static <T extends Enum<T> & RsqlQueryField> Node parse(final String rsql, final Class<T> rsqlQueryFieldType) {
|
||||
return parse(rsql, key -> resolveKey(key, rsqlQueryFieldType));
|
||||
return parse(rsql, rsqlQueryFieldType == null ? null : key -> resolveKey(key, rsqlQueryFieldType));
|
||||
}
|
||||
|
||||
private static Node parse(final String rsql, final Function<String, Key> keyResolver) {
|
||||
try {
|
||||
return PARSER
|
||||
.parse(RsqlConfigHolder.getInstance().isCaseInsensitiveDB() || RsqlConfigHolder.getInstance().isIgnoreCase()
|
||||
? rsql.toLowerCase()
|
||||
: rsql)
|
||||
.parse(rsql)
|
||||
.accept(new RsqlVisitor(keyResolver));
|
||||
} catch (final RSQLParserException e) {
|
||||
throw new RSQLParameterSyntaxException(e);
|
||||
|
||||
@@ -152,11 +152,9 @@ public class SpecificationBuilder<T> {
|
||||
} else {
|
||||
final MapJoin<?, ?, ?> mapPath = (MapJoin<?, ?, ?>) pathResolver.getPath(attribute);
|
||||
final Path<String> valuePath = (Path<String>) mapPath.value();
|
||||
return cb.and(
|
||||
equal(mapPath.key(), split[1]),
|
||||
isNot(op)
|
||||
? notEqualInLike(comparison, (PluralAttribute<?, ?, ?>) attribute, null)
|
||||
: compare(comparison, valuePath));
|
||||
return isNot(op)
|
||||
? compare(comparison, pathResolver.getJoinOnInner(attribute, split[1]))
|
||||
: cb.and(equal(mapPath.key(), split[1]), compare(comparison, valuePath));
|
||||
}
|
||||
} else if (attribute instanceof SetAttribute<?, ?> setAttribute) {
|
||||
if (split.length < 2 || ObjectUtils.isEmpty(split[1])) {
|
||||
@@ -413,6 +411,10 @@ public class SpecificationBuilder<T> {
|
||||
return getCollectionPathResolver(attribute.getName()).getJoinOn(value);
|
||||
}
|
||||
|
||||
private MapJoin<?, ?, ?> getJoinOnInner(final Attribute<?, ?> attribute, final Object value) {
|
||||
return getCollectionPathResolver(attribute.getName()).getJoinOnInner(value);
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
attributeToPathResolver.values().forEach(CollectionPathResolver::reset);
|
||||
}
|
||||
@@ -428,6 +430,7 @@ public class SpecificationBuilder<T> {
|
||||
private final List<Path<?>> paths = new ArrayList<>();
|
||||
private int pos;
|
||||
private final Map<Object, MapJoin<?, ?, ?>> joinOnCache = new HashMap<>();
|
||||
private final Map<Object, MapJoin<?, ?, ?>> joinOnInnerCache = new HashMap<>();
|
||||
|
||||
private CollectionPathResolver(final String attributeName) {
|
||||
this.attributeName = attributeName;
|
||||
@@ -452,6 +455,14 @@ public class SpecificationBuilder<T> {
|
||||
});
|
||||
}
|
||||
|
||||
private MapJoin<?, ?, ?> getJoinOnInner(final Object value) {
|
||||
return joinOnInnerCache.computeIfAbsent(value, k -> {
|
||||
final MapJoin<?, ?, ?> mapPath = (MapJoin<?, ?, ?>) root.join(attributeName, JoinType.INNER);
|
||||
mapPath.on(equal(mapPath.key(), k));
|
||||
return mapPath;
|
||||
});
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user