Sayak Mukhopadhyay
Sayak Mukhopadhyay

Reputation: 1464

Spring Data QuerydslBinderCustomizer when no filter parameters are passed

I am implementing a Repository which implements QuerydslBinderCustomizer to help create Predicates from the url filter parameters. It is as such

@NoRepositoryBean
public interface TableRepository<T extends TableEntity, S extends EntityPathBase<T>> extends PagingAndSortingRepository<T, UUID>, QuerydslPredicateExecutor<T>, QuerydslBinderCustomizer<S> {
    @Override
    default void customize(@NonNull QuerydslBindings bindings, @NonNull S root) {
        bindings.bind(String.class).all((MultiValueBinding<StringPath, String>) (path, values) -> {
            BooleanBuilder predicate = new BooleanBuilder();
            values.forEach(value -> predicate.or(path.containsIgnoreCase(value)));
            return Optional.of(predicate);
        });
    }
}

My individual entity repositories are extending from this like,

public interface UserRepository extends TableRepository<User, QUser> {
}

This gives rise to an endpoint http://localhost:port/users. Things are working fine and I can make requests such as http://localhost:port/users?name="somename".

Now, I am implementing soft deletes and I don't want to return soft deleted records on querying. I was planning on editing the customize method for this like

default void customize(@NonNull QuerydslBindings bindings, @NonNull S root) {
        bindings.bind(String.class).all((MultiValueBinding<StringPath, String>) (path, values) -> {
            BooleanBuilder predicate = new BooleanBuilder();
            values.forEach(value -> predicate.or(path.containsIgnoreCase(value)));
            // If path does not have delete flag, add deleted=false to the predicate
            return Optional.of(predicate);
        });
    }

But the issue arises when a call is made to the endpoint without any parameter i.e. at http://localhost:port/users. In this case bindings.bind() gets executed but BooleanBuilder predicate = new BooleanBuilder(); part is not. Thus I am unable to add the required predicate to filter out soft deleted records. How to proceed?

I might be doing the filtering of deleted records the wrong way. Is there a better alternative?

EDIT: As requested, I am linking a basic repository https://github.com/SayakMukhopadhyay/demo-querydsl

Upvotes: 2

Views: 1781

Answers (1)

js.palus
js.palus

Reputation: 83

So I imagine that you found a solution by now. But for those who try to get some sort of "default predicate" here is a potential solution.

Just like in your case, we also had a soft delete and wanted to exclude them by default unless specifically requested.

First of all, you can't set this in the customize method. these binding only trigger when there is a value passed for a criteria. For some reasons they didn't provide a bindings for when there is no value. they probably had good reasons to do this way. So in this method you only care about the criteria passed.

The way we handled default values in our request was to modify the Predicate before calling findAll.

The Controller

The Controller signature should look like this, with a predicate:

@GetMapping
public ResponseEntity<List<User>> searchUsers(
            @QuerydslPredicate(root = User.class, bindings = UserRepository.class) Predicate predicate) 

The Service

All you need is to check if the predicate has the criteria in it, and if not then add it yourself.

if (!ExpressionUtils.extract(predicate).toString().contains("deleted =")) {
    predicate = new StringPath("deleted").eq("false").and(predicate);
}
userRepository.findAll(predicate);

That should add the criteria if not there and ignore if it is.

This is obviuosly a simplified representation but the idea is there.

Hope this helps.

Upvotes: 2

Related Questions