
io.github.perplexhub.rsql.RSQLJPAPredicateConverter Maven / Gradle / Ivy
package io.github.perplexhub.rsql;
import static io.github.perplexhub.rsql.RSQLOperators.*;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.persistence.criteria.*;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.Attribute.PersistentAttributeType;
import javax.persistence.metamodel.ManagedType;
import cz.jirutka.rsql.parser.ast.AndNode;
import cz.jirutka.rsql.parser.ast.ComparisonNode;
import cz.jirutka.rsql.parser.ast.ComparisonOperator;
import cz.jirutka.rsql.parser.ast.OrNode;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@SuppressWarnings({ "rawtypes", "unchecked" })
public class RSQLJPAPredicateConverter extends RSQLVisitorBase {
private final CriteriaBuilder builder;
private final Map cachedJoins = new HashMap<>();
private final @Getter Map propertyPathMapper;
private final @Getter Map> customPredicates;
public RSQLJPAPredicateConverter(CriteriaBuilder builder, Map propertyPathMapper) {
this(builder, propertyPathMapper, null);
}
public RSQLJPAPredicateConverter(CriteriaBuilder builder, Map propertyPathMapper, List> customPredicates) {
super();
this.builder = builder;
this.propertyPathMapper = propertyPathMapper != null ? propertyPathMapper : Collections.emptyMap();
this.customPredicates = customPredicates != null ? customPredicates.stream().collect(Collectors.toMap(RSQLCustomPredicate::getOperator, Function.identity(), (a, b) -> a)) : Collections.emptyMap();
}
RSQLJPAContext findPropertyPath(String propertyPath, Path startRoot) {
ManagedType> classMetadata = getManagedType(startRoot.getJavaType());
Path> root = startRoot;
Class type = startRoot.getJavaType();
Attribute, ?> attribute = null;
String[] properties = propertyPath.split("\\.");
for (String property : properties) {
String mappedProperty = mapProperty(property, classMetadata.getJavaType());
if (!mappedProperty.equals(property)) {
RSQLJPAContext context = findPropertyPath(mappedProperty, root);
root = context.getPath();
attribute = context.getAttribute();
} else {
if (!hasPropertyName(mappedProperty, classMetadata)) {
throw new IllegalArgumentException("Unknown property: " + mappedProperty + " from entity " + classMetadata.getJavaType().getName());
}
if (isAssociationType(mappedProperty, classMetadata)) {
boolean isOneToOneAssociationType = isOneToOneAssociationType(mappedProperty, classMetadata);
Class> associationType = findPropertyType(mappedProperty, classMetadata);
type = associationType;
String previousClass = classMetadata.getJavaType().getName();
classMetadata = getManagedType(associationType);
String keyJoin = root.getJavaType().getSimpleName().concat(".").concat(mappedProperty);
log.debug("Create a join between [{}] and [{}] using key [{}]", previousClass, classMetadata.getJavaType().getName(), keyJoin);
root = isOneToOneAssociationType ? joinLeft(keyJoin, root, mappedProperty) : join(keyJoin, root, mappedProperty);
} else if (isElementCollectionType(mappedProperty, classMetadata)) {
String previousClass = classMetadata.getJavaType().getName();
attribute = classMetadata.getAttribute(property);
classMetadata = getManagedElementCollectionType(mappedProperty, classMetadata);
String keyJoin = root.getJavaType().getSimpleName().concat(".").concat(mappedProperty);
log.debug("Create a element collection join between [{}] and [{}] using key [{}]", previousClass, classMetadata.getJavaType().getName(), keyJoin);
root = join(keyJoin, root, mappedProperty);
} else {
log.debug("Create property path for type [{}] property [{}]", classMetadata.getJavaType().getName(), mappedProperty);
root = root.get(mappedProperty);
if (isEmbeddedType(mappedProperty, classMetadata)) {
Class> embeddedType = findPropertyType(mappedProperty, classMetadata);
type = embeddedType;
classMetadata = getManagedType(embeddedType);
} else {
attribute = classMetadata.getAttribute(property);
}
}
}
}
accessControl(type, attribute.getName());
return RSQLJPAContext.of(root, attribute);
}
protected Path> join(String keyJoin, Path> root, String mappedProperty) {
log.info("join(keyJoin:{},root:{},mappedProperty:{})", keyJoin, root, mappedProperty);
if (cachedJoins.containsKey(keyJoin)) {
root = cachedJoins.get(keyJoin);
} else {
root = ((From) root).join(mappedProperty);
cachedJoins.put(keyJoin, root);
}
return root;
}
protected Path> joinLeft(String keyJoin, Path> root, String mappedProperty) {
log.info("joinLeft(keyJoin:{},root:{},mappedProperty:{})", keyJoin, root, mappedProperty);
if (cachedJoins.containsKey(keyJoin)) {
root = cachedJoins.get(keyJoin);
} else {
root = ((From) root).join(mappedProperty, JoinType.LEFT);
cachedJoins.put(keyJoin, root);
}
return root;
}
@Override
public Predicate visit(ComparisonNode node, Root root) {
log.debug("visit(node:{},root:{})", node, root);
ComparisonOperator op = node.getOperator();
RSQLJPAContext holder = findPropertyPath(mapPropertyPath(node.getSelector()), root);
Path attrPath = holder.getPath();
Attribute attribute = holder.getAttribute();
Class type = attribute.getJavaType();
if (attribute.getPersistentAttributeType() == PersistentAttributeType.ELEMENT_COLLECTION) {
type = getElementCollectionGenericType(type, attribute);
}
if (type.isPrimitive()) {
type = primitiveToWrapper.get(type);
} else if (RSQLJPASupport.getValueTypeMap().containsKey(type)) {
type = RSQLJPASupport.getValueTypeMap().get(type); // if you want to treat Enum as String and apply like search, etc
}
if (customPredicates.containsKey(op)) {
RSQLCustomPredicate> customPredicate = customPredicates.get(op);
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy