написать get запрос с возможностью фильтрации по полям
В поле запроса получаю данные по @RequestParam,
@GetMapping(value = "/get_transactions_by_comment_and_amount")
public List<TransactionDto> getTransactionsByCommentAndAmount(@RequestParam(value = "comment", required = false) String comment,
@RequestParam(value = "amount", required = false) Long amount) {
return transactionService.getTransactionsFilteredByCommentAndAmount(comment, amount);
}
Интересует сам запрос в БД
public interface TransactionRepository extends JpaRepository<Transaction, Long> {
List<Transaction> findTransactionsByCommentAndAmount(String comment, Long amount);
}
В моем случе запрос с AND или OR не подходит. При наличии обоих параметров, фильтрация должна производиться по обоим. При наличии одного параметра - фильтрация по одному, при отсутствии входящих параметров - получение всех записей.
Ответы (3 шт):
Тебе нужно добавить отдельный метод с кастомным @Query
public interface TransactionRepository extends
JpaRepository<Transaction, Long> {
@Query("""
SELECT t FROM Transaction t WHERE
(:comment is null or t.comment = :comment) and
(:amount is null or t.amount = :amount)
""")
List<Transaction> findAllByFilter(String comment, Long amount);
}
Нужно написать свою собственную реализацию метода и самому проверять пришедшие поля на null
Интерфейс
public interface TransactionRepository extends JpaRepository<Transaction, Long>, TransactionRepositoryCustom {
}
Свой собственный интерфейс
public interface TransactionRepositoryCustom {
List<Transaction> findTransactionsByCommentAndAmount(String comment, Long amount);
}
Реализация
@Repository
@RequiredArgsConstructor
public class TransactionRepositoryImpl implements TransactionRepositoryCustom {
private final static String COMMENT_FIELD_NAME = "comment";
private final static String AMOUNT_FIELD_NAME = "amount";
private final EntityManager entityManager;
@Override
public List<Transaction> findTransactionsByCommentAndAmount(String comment, Long amount) {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<Transaction> cq = cb.createQuery(Transaction.class);
Root<Transaction> root = cq.from(Transaction.class);
addEqualPredicate(cb, root, COMMENT_FIELD_NAME, comment);
addEqualPredicate(cb, root, AMOUNT_FIELD_NAME, amount);
final TypedQuery<Transaction> query = entityManager.createQuery(cq);
return query.getResultList();
}
private void addEqualPredicate(CriteriaBuilder cb, Root<Transaction> root, String name, Object value) {
if (value != null) {
cb.equal(root.get(name), value);
}
}
}
Использование
@Autowired
private TransactionRepository transactionRepository;
Еще один вариант, это использовать спецификации. Они позволяют динамически конструировать предикаты.
Сначала расширяем наш репозиторий от JpaSpecificationExecutor:
public interface TransactionRepository
extends JpaRepository<Transaction, Long>, JpaSpecificationExecutor<Transaction> { }
Далее определяем базовые предикаты (это можно сделать в любом классе или интерфейсе, в том же TransactionRepository):
import org.springframework.data.jpa.domain.Specification;
...
static Specification<Transaction> hasComment(String comment) {
return (transaction, cq, cb) -> {
if (comment == null) {
return cb.isTrue(cb.literal(true));
} else {
return cb.equal(transaction.get("comment"), comment);
}
}
}
static Specification<Transaction> hasAmount(Long amount) {
return (transaction, cq, cb) -> {
if (amount == null) {
return cb.isTrue(cb.literal(true));
} else {
return cb.equal(transaction.get("amount"), amount);
}
}
}
Теперь в месте вызова можно делать так:
import static org.springframework.data.jpa.domain.Specification.where;
...
transactionRepository.findAll(where(hasComment(comment)));
// или
transactionRepository.findAll(where(hasComment(comment)).and(hasAmount(amount)));
В данном случае, логика обработки null находится внутри hasComment и hasAmount, но ее можно вынести и наружу, чтоб этим занимался клиент transactionRepository и строил спецификацию динамически:
static Specification<Transaction> hasComment(String comment) {
return (transaction, cq, cb) -> cb.equal(transaction.get("comment"), comment);
}
static Specification<Transaction> hasAmount(Long amount) {
return (transaction, cq, cb) -> cb.equal(transaction.get("amount"), amount);
}
}
}
...
Specification<Transaction> txFilter = someInitialFilter;
if (comment != null) {
txFilter = txFilter.and(hasComment(comment));
}
if (amount != null) {
txFilter = txFilter.and(hasAmount(amount));
}
transactionRepository.findAll(txFilter);