Обрабатывать дополнительные параметры в QueryDSL

Я использую QueryDSL с SpringData. У меня стол говорят,Employee и я создал класс сущности, скажем,EmployeeEntity Я написал следующее сервис метод

public EmployeeEntity getEmployees(String firstName, String lastName)
{
    QEmployeeEntity employee = QEmployeeEntity.employeeEntity;
    BooleanExpression query = null;
    if(firstName != null)
    {
        query = employee.firstName.eq(firstName);
    }
    if(lastName != null)
    {
        query = query.and(employee.lastName.eq(lastName)); // NPException if firstName is null as query will be NULL
    }
    return empployeeDAO.findAll(query);
}

как и выше, я прокомментировал NPException. Как использовать QueryDSL дополнительные параметры в QueryDSL использование данных Spring?

спасибо :)

7 ответов


BooleanBuilder может использоваться в качестве динамического построителя для логических выражений:

public EmployeeEntity getEmployees(String firstName, String lastName) {
    QEmployeeEntity employee = QEmployeeEntity.employeeEntity;
    BooleanBuilder where = new BooleanBuilder();
    if (firstName != null) {
        where.and(employee.firstName.eq(firstName));
    }
    if (lastName != null) {
        where.and(employee.lastName.eq(lastName));
    }
    return empployeeDAO.findAll(where);
}

BooleanBuilder хорош. Вы также можете обернуть его и добавить "необязательные" методы, чтобы избежать условий if:

например, для "и" вы можете написать: (используются Java 8 lambdas)

public class WhereClauseBuilder implements Predicate, Cloneable
{
    private BooleanBuilder delegate;

    public WhereClauseBuilder()
    {
        this.delegate = new BooleanBuilder();
    }

    public WhereClauseBuilder(Predicate pPredicate)
    {
        this.delegate = new BooleanBuilder(pPredicate);
    }

    public WhereClauseBuilder and(Predicate right)
    {
        return new WhereClauseBuilder(delegate.and(right));
    }

    public <V> WhereClauseBuilder optionalAnd(@Nullable V pValue, LazyBooleanExpression pBooleanExpression)
    {
        return applyIfNotNull(pValue, this::and, pBooleanExpression);
    }

    private <V> WhereClauseBuilder applyIfNotNull(@Nullable V pValue, Function<Predicate, WhereClauseBuilder> pFunction, LazyBooleanExpression pBooleanExpression)
    {
        if (pValue != null)
        {
            return new WhereClauseBuilder(pFunction.apply(pBooleanExpression.get()));
        }

        return this;
    }
   }

    @FunctionalInterface
    public interface LazyBooleanExpression
    {
        BooleanExpression get();
    }

и тогда использование будет намного чище:

public EmployeeEntity getEmployees(String firstName, String lastName) {
    QEmployeeEntity employee = QEmployeeEntity.employeeEntity;

    return empployeeDAO.findAll
    (
       new WhereClauseBuilder()
           .optionalAnd(firstName, () -> employee.firstName.eq(firstName))
           .optionalAnd(lastName, () -> employee.lastName.eq(lastName))
    );
}

можно также использовать дополнительный класс JDK


это Java 101 на самом деле: проверка null и инициализировать запрос вместо объединения предикатов. Таким образом, такой вспомогательный метод может сделать трюк:

private BooleanExpression createOrAnd(BooleanExpression left, BooleanExpression right) {
  return left == null ? right : left.and(right);
}

затем вы можете просто сделать:

BooleanExpression query = null;

if (firstName != null) {
  query = createOrAnd(query, employee.firstName.eq(firstName));
}

if (lastName != null) {
  query = createOrAnd(query, employee.lastName.eq(lastName));
}

…

обратите внимание, что я использую createOrAnd(…) даже в первом предложении просто для согласованности и не нужно адаптировать этот код, если вы решите добавить новое предложение даже до одного для firstName.


база на то, что вам нужно, я бы сделал это

public List<EmployeeEntity> getEmployees(Optional<String> firstName, Optional<String> lastName)
{
    BooleanExpression queryPredicate =  QEmployeeEntity.employeeEntity.firstName.containsIgnoreCase(firstName.orElse("")).and(QEmployeeEntity.employeeEntity.lastName.containsIgnoreCase(lastName.orElse(""))); 
    return empployeeDAO.findAll(queryPredicate);
}

прежде всего вы должны вернуть List of EmployeeEntity. Во-вторых, его лучше использовать дополнительно, чем проверять, если его null, и Вы можете передать Java 8 Optional значения, полученные из optional RequestParam вроде этого:

@RequestMapping(value = "/query", method = RequestMethod.GET)
public ModelAndView queryEmployee(@RequestParam(value = "firstName", required = false) Optional<String> firstName, @RequestParam(value = "lastName", required = false) Optional<String> lastName) 
{
       List<EmployeeEntity> result =  getEmployees(firstName, lastName);    
            ....
}

и очень важно использовать containsIgnoreCase функция в предикате: его лучше, чем типичный like причина его нечувствительности к регистру.

на мой взгляд вы следует использовать такой подход:

@Controller
class UserController {

  @Autowired UserRepository repository;

  @RequestMapping(value = "/", method = RequestMethod.GET)
  String index(Model model, @QuerydslPredicate(root = User.class) Predicate predicate,    
          Pageable pageable, @RequestParam MultiValueMap<String, String> parameters) {

    model.addAttribute("users", repository.findAll(predicate, pageable));

    return "index";
  }
}

посмотрите на здесь.


Если вы проверяете реализацию QueryDSL null:

public BooleanExpression and(@Nullable Predicate right) {
    right = (Predicate) ExpressionUtils.extract(right);
    if (right != null) {
        return BooleanOperation.create(Ops.AND, mixin, right);
    } else {
        return this;
    }
}

который, предположительно, то, что вы хотите.


Это очень простой способ справиться с дополнительными параметрами, я использую его в своем проекте:

    public List<ResultEntity> findByOptionalsParams(String param1, Integer param2) {
    QResultEntity qResultEntity = QResultEntity.resultEntity;
    final JPQLQuery<ResultEntity> query = from(qResultEntity);
    if (!StringUtils.isEmpty(param1)) {
      query.where(qResultEntity.field1.like(Expressions.asString("%").concat(param1).concat("%")));
    }
    if (param2 != null) {
      query.where(qResultEntity.field2.eq(param2));
    }
    return query.fetch();
}

я столкнулся с той же проблемой и вот еще вариант Тимо Westkämper ' s принято отвечать С помощью Optional.

default Optional<Correlation> findOne(
        @Nonnull final String value, @Nullable final String environment,
        @Nullable final String application, @Nullable final String service) {
    final QSome Some = QSome.some;
    final BooleanBuilder builder = new BooleanBuilder();
    ofNullable(service).map(some.service::eq).map(builder::and);
    ofNullable(application).map(some.application::eq).map(builder::and);
    ofNullable(environment).map(some.environment::eq).map(builder::and);
    builder.and(some.value.eq(value));
    return findOne(builder);
}