Traverse Path with Criteria Root element of unkown object structure

Viewed 375

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.

2 Answers

Collections of nested entities need to be fetched eagerly if you want immediate access, ie within your query.

    @OneToMany(fetch = FetchType.EAGER) 
    @Column
    private Set<EntityC> someReferences;

Note "someReference.someProperty" and so on is not null safe you will get unexpected results if "someReference" is null. Figuring out how to use Join like the comments above is recommended to avoid this.

Related