Add support for plugable QL for EntityManager (#2698)

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-09-26 15:39:21 +03:00
committed by GitHub
parent 4434484d35
commit 1abfa0a2f4
10 changed files with 45 additions and 42 deletions

View File

@@ -31,7 +31,6 @@ import java.util.Objects;
import java.util.function.BiPredicate;
import org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlParser;
/**
* Provides entity matcher that matches an entity object against a filter (a {@link Node} or an RSQL string).
@@ -44,14 +43,10 @@ public class EntityMatcher {
this.root = root;
}
public static EntityMatcher forNode(final Node root) {
public static EntityMatcher of(final Node root) {
return new EntityMatcher(root);
}
public static EntityMatcher forRsql(final String rsql) {
return forNode(RsqlParser.parse(rsql));
}
public <T> boolean match(final T t) {
return match(t, root);
}

View File

@@ -149,8 +149,7 @@ public class QLSupport {
* given {@code fieldNameProvider}
* @throws RSQLParameterSyntaxException if the RSQL syntax is wrong
*/
public <A extends Enum<A> & QueryField, T> Specification<T> buildSpec(
final String query, final Class<A> queryFieldType) {
public <A extends Enum<A> & QueryField, T> Specification<T> buildSpec(final String query, final Class<A> queryFieldType) {
if (specBuilder == SpecBuilder.G3) {
return new SpecificationBuilder<T>(!caseInsensitiveDB && ignoreCase, database)
.specification(parser.parse(caseInsensitiveDB || ignoreCase ? query.toLowerCase() : query, queryFieldType));
@@ -159,6 +158,10 @@ public class QLSupport {
}
}
public <A extends Enum<A> & QueryField> EntityMatcher entityMatcher(final String query, final Class<A> queryFieldType) {
return EntityMatcher.of(parser.parse(caseInsensitiveDB || ignoreCase ? query.toLowerCase() : query, queryFieldType));
}
/**
* Validates the query string
*
@@ -168,7 +171,7 @@ public class QLSupport {
* @throws QueryException if query is invalid
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public <A extends Enum<A> & QueryField> void validateQuery(final String query, final Class<A> queryFieldType, final Class<?> jpaType) {
public <A extends Enum<A> & QueryField> void validate(final String query, final Class<A> queryFieldType, final Class<?> jpaType) {
final CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
final CriteriaQuery<?> criteriaQuery = criteriaBuilder.createQuery(jpaType);
buildSpec(query, queryFieldType).toPredicate(criteriaQuery.from((Class) jpaType), criteriaQuery, criteriaBuilder);

View File

@@ -50,14 +50,14 @@ public class SpecificationBuilderLegacy<A extends Enum<A> & QueryField, T> {
public Specification<T> specification(final String rsql) {
return (root, query, cb) -> {
final QLSupport rsqlUtility = QLSupport.getInstance();
final QLSupport qlSupport = QLSupport.getInstance();
final Node rootNode = parseRsql(rsql, rsqlUtility);
final Node rootNode = parseRsql(rsql, qlSupport);
query.distinct(true);
final boolean ensureIgnoreCase = !rsqlUtility.isCaseInsensitiveDB() && rsqlUtility.isIgnoreCase();
final boolean ensureIgnoreCase = !qlSupport.isCaseInsensitiveDB() && qlSupport.isIgnoreCase();
final RSQLVisitor<List<Predicate>, String> jpqQueryRSQLVisitor =
rsqlUtility.getSpecBuilder() == LEGACY_G1
qlSupport.getSpecBuilder() == LEGACY_G1
? new JpaQueryRsqlVisitor<>(root, cb, rsqlQueryFieldType, virtualPropertyReplacer, database, query, ensureIgnoreCase)
: new JpaQueryRsqlVisitorG2<>(rsqlQueryFieldType, root, query, cb, database, virtualPropertyReplacer, ensureIgnoreCase);
final List<Predicate> accept = rootNode.accept(jpqQueryRSQLVisitor);

View File

@@ -553,7 +553,7 @@ class SpecificationBuilderTest {
private List<Root> filter(final String rsql) {
// reference / auto filter (using elements and reflection)
final EntityMatcher matcher = EntityMatcher.forRsql(rsql);
final EntityMatcher matcher = EntityMatcher.of(RsqlParser.parse(rsql));
final List<Root> refResult = StreamSupport.stream(rootRepository.findAll().spliterator(), false).filter(matcher::match).toList();
final List<Root> result = rootRepository.findAll(getSpecification(rsql));
// auto check with reference result