Search code examples
javapersistencecriteria-api

Traverse Path with Criteria Root element of unkown object structure


I have a search API that can abstractly search for properties of referenced entities defined in a persistence scheme. For example i have something like these entities:

@Entity
public class EntityA {
@Column
private String someProperty;

@Column
private EntityB someReference;
}

@Entity
public class EntityB {
@Column
private String someProperty;

@Column
private Set<EntityC> someReferences;
}

@Entity
public class EntityC {
@Column
private String someProperty;
}

With these entities i can traverse paths (for example when my root is EntityA and im sure that the user searches for a string field within:

private Expression<String> getExpression(Root<T> root, String fieldName) {
        String[] propertySplit = fieldName.split("\\.");
        Path<String> path = null;
        for (String property : propertySplit) {
            if (path == null) {
                path = root.get(property);
                continue;
            }
            path = path.get(property);
        }
        return path;
    }

Assuming that is currently EntityA i can call the getExpression method like this:

[...]
criteriaBuilder.equal(getExpression(entityARoot, "someProperty"), "myValue");
[...]

And i also can invoke references:

[...]
criteriaBuilder.equal(getExpression(entityARoot, "someReference.someProperty"), "myValue");
[...]

But when the path encounters a Collection Type, then this doesn't work, but i want to do something like this:

[...]
criteriaBuilder.equal(getExpression(entityARoot, "someReference.someReferences.someProperty"), "myValue");
[...]

Im getting the following exception:

java.lang.IllegalStateException: Illegal attempt to dereference path source [null.someReferences] of basic type

I know that there must be a way to archive that since Spring Data can do that with Method-Names in Repositories too. My goal is to create a function that can traverse any unknown object as long as the final attribute i am checking is the type i know of. So i know every time whether the criteria should compare String, Integer, Boolean, Date etc.


Solution

  • I stumbled upon an implementation that I think is what you are looking for. Check out code for this project github.com/perplexhub/rsql-jpa-specification/ ... /RSQLJPAPredicateConverter.java#L49-L130.

    I bookmarked this project as something I can import into my existing project, to enable generic handling as you have described in your question. My current implementation is hard coded against all known input predicates.