Writing a clean Predicate Builder
You could follow the below approach:
public class PredicateBuilder {

    private Predicate predicate;
    private CriteriaBuilder cb;
    private Runnable[] tasks;
    
    private static final PredicateBuilder instance = new PredicateBuilder();
    private PredicateBuilder(){
    }
    
    public static PredicateBuilder with(Predicate p, CriteriaBuilder c) {
        instance.predicate = p;
        instance.cb = c;
        return instance;
    }

    public PredicateBuilder tasks(Runnable... tasks) {
        instance.tasks = tasks;
        return instance;
    }

    public Predicate build() {
        Arrays.stream(instance.tasks)
              .forEach(Runnable::run);
        return instance.predicate;
    }
    
    public static void like(Path&lt;String&gt; path, String value) {
        if (StringUtils.isNotEmpty(value)) {
            CriteriaBuilder c = instance.cb;
            instance.predicate = c.and(instance.predicate,
                                       c.like(c.lower(path), &quot;%&quot; + value.toLowerCase() + &quot;%&quot;));
        }
    }

    public static &lt;T&gt; void eq(Path&lt;T&gt; path, T value) {
        if (value != null) {
            CriteriaBuilder c = instance.cb;
            instance.predicate = c.and(instance.predicate, c.equal(path, value));
        }
    }

    public static &lt;T extends Enum&lt;T&gt;&gt; void eqEnumVal(Path&lt;T&gt; path, Class&lt;T&gt; enumType, String name) {
        if (StringUtils.isNotEmpty(name)) {
            instance.predicate = eq(instance.predicate, instance.cb, path, Enum.valueOf(enumType, name));
        }
    }

}       

Then you could invoke as below:
return PredicateBuilder.with(predicate, cb)
                       .tasks(() -&gt; eqEnumVal(root.get(regType), RegType.class, filter.getRegType()),
                              () -&gt; eq(userJoin.get(Acct_.id), filter.getAcctId()),
                              () -&gt; like(root.get(regNumber), filter.getRegNumber())
                       .build();

```