I'm trying to implement properly search functionality for database table. I tried this approach:
Controller:
@GetMapping
public Page<TransactionDTO> find(TransactionFilterDTO filter, Pageable page) {
return searchRepository
.findTransactionsByFilter(mapper.toFilter(filter), page)
.map(mapper::toDTO);
}
Filer DTO:
public class TransactionFilterDTO {
private String name;
private Integer id;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private LocalDateTime from;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private LocalDateTime to;
... // getters and setter
}
Search implementation:
@Repository
public class TransactionSearchRepositoryImpl implements TransactionSearchRepository {
@Autowired
private TransactionRepository transactionRepository;
@Autowired
private TransactionSpecification specification;
@Override
public Page<Transaction> findTransactionsByFilter(TransactionFilter filter, @Nullable Pageable page) {
List<Transaction> transactions = transactionRepository
.findAll(specification.getFilter(filter));
int totalCount = transactions.size();
if(page != null) {
transactions = transactions
.stream()
.skip(page.getOffset())
.limit(page.getPageSize())
.collect(Collectors.toList());
}
return new PageImpl<>(transactions, page, totalCount);
}
}
Repository:
public interface TransactionSearchRepository {
Page<Transaction> findTransactionsByFilter(TransactionFilter filter, Pageable page);
}
Is there some better way to implement a search functionality? This solution is very ugly in my view.
1 Answer 1
In JPA you could use JpaSpecificationExecutor<T>
in repository, with that you could utilize it's method.
TransactionRepository
public interface TransactionRepository extends JpaRepository<Transaction, Long>,
JpaSpecificationExecutor<Transaction> {
}
If you look at the source of JpaSpecificationExecutor
it has,
Page< T > findAll(@Nullable Specification<T> spec, Pageable pageable);
With that you just pass the specification and Pageable to return what you expected.
Specification
@AllArgsConstructor
class TransactionSpecification implements Specification<Transaction> {
private TransactionFilter transactionFilter;
@Override
public Predicate toPredicate(Root<DataImport> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
// You could add multiple Predicates based on the transactionFilter
return criteriaBuilder.equal(root.get("table_name"), "value");
}
}
Controller
@Autowired
private TransactionRepository transactionRepository;
@GetMapping
public Page<TransactionDTO> find(TransactionFilterDTO filter, Pageable page) {
TransactionFilter transactionFilter = mapper.toFilter(filter);
return transactionRepository.findAll(new TransactionSpecification(transactionFilter), page);
}
I am pretty late answering but you could give it a try