rjdkolb
rjdkolb

Reputation: 11888

Easy way of chaining QueryDSL for restful get

I want to do a simple conditional JPA query to get a list of BankTransactions from the bankAccountNumber and branchCode.

My cases are

  1. get("12345678","123") gets all the transactions for bank account 12345678 and branch 123

  2. get(null,"123") gets all the transactions for all bank accounts and branch 123

  3. get(null,null) gets all the transactions

I've written this code, but it seems really smelly since there is no initial value for BooleanExpression.

I think I am misunderstanding the API. How do I clean this up ?

(Am I misunderstanding the API ?)

public Page<BankTransactions> get(
  @RequestParam(required = false) String bankAccountNumber,
  @RequestParam(required = false) String branchCode,
  Pageable pageable) {

  QBankTransactions q = QBankTransactions.bankTransactions;
  BooleanExpression expression = null;

  if (bankAccountNumber != null) {
    if (expression == null) {
      expression = q.bankAccountNumber.eq(bankAccountNumber);;
    } else {
      expression.and(q.bankAccountNumber.eq(bankAccountNumber));
    }
  }
  if (branchCode != null) {
    if (expression == null) {
      expression = q.branchCode.eq(branchCode);
    } else {
      expression.and(q.branchCode.eq(branchCode));
    }
  }
  if (expression != null) {
    Page<BankTransactions> response = repository.findAll(expression, pageable);
    return response;
  } else {
    Page<BankTransactions> response = repository.findAll(pageable);
    return response;
  }
}

Upvotes: 1

Views: 651

Answers (3)

Koman
Koman

Reputation: 377

This is my solution using QueryDslPredicateExecutor (which allows the execution of the Predicate) & QueryDslBindings (which allows path specific bindings - API).

So for example below if I tried looking up description of "FindMe" using "ind" or "Ind" it'll return the result in both instances correctly for "FindMe. Bindings won't be bound that are not found on the predicate from the HTTPRequest.

public interface BankTransactionsRepository extends PagingAndSortingRepository<BankTransactions, Long>,                                             
QueryDslPredicateExecutor<BankTransactions>,                                                
QuerydslBinderCustomizer<QBankTransactions> {

  @Override
  default public void customize(QuerydslBindings bindings, 
                                QBankTransactions root) {
      bindings.bind(root.description).first((path, value) -> path.containsIgnoreCase(value));
      bindings.bind(root.template.code).first((path, value) -> path.containsIgnoreCase(value));
      bindings.bind(root.status).first((path, value) -> path.containsIgnoreCase(value));
      bindings.bind(root.processedDate).first((path, value) -> path.eq(value));
  }    
}

Upvotes: 1

mr-m-jali
mr-m-jali

Reputation: 324

Simple pass a predicate into the method parameter. More info.

public Page<BankTransactions> get(
  @QuerydslPredicate(root = BankTransactions.class) Predicate predicate,    
      Pageable pageable, @RequestParam MultiValueMap<String, String> parameters) {
    Page<BankTransactions> response = repository.findAll(predicate, pageable);
    return response;

}

Upvotes: 1

rjdkolb
rjdkolb

Reputation: 11888

I really don't like this answer, but at least it makes my code look cleaner. I added helper methods (eq) for each data type.

A least now my code is one line per input discriminator.

public Page<BankTransactions> get(
  @RequestParam(required = false) String bankAccountNumber,
  @RequestParam(required = false) String branchCode,
  Pageable pageable) {

  QBankTransactions q = QBankTransactions.bankTransactions;
  BooleanExpression expression = null;

  expression = eq(bankAccountNumber, expression, q.bankAccountNumber);
  expression = eq(branchCode, expression, q.branchCode);

  if (expression != null) {
    Page<BankTransactions> response = repository.findAll(expression, pageable);
    return response;
  } else {
    Page<BankTransactions> response = repository.findAll(pageable);
    return response;
  }
}

private BooleanExpression eq(String inputString, BooleanExpression expression, StringPath path) {
  if (inputString != null) {
    if (expression == null) {
      expression = path.eq(inputString);
    } else {
      expression.and(path.eq(inputString));
    }
  }
  return expression;
}

private BooleanExpression eq(Long inputLong, BooleanExpression expression, NumberPath path) {
  if (inputLong != null) {
    if (expression == null) {
      expression = path.eq(inputLong);
    } else {
      expression.and(path.eq(inputLong));
    }
  }
  return expression;
}

Upvotes: 0

Related Questions