Repository, Service e Controller
Repository
@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
@Query(nativeQuery = true, value = """
SELECT DISTINCT tb_product.id, tb_product.name
FROM tb_product
INNER JOIN tb_product_category ON tb_product_category.product_id = tb_product.id
WHERE (:categoryIds IS NULL OR tb_product_category.category_id IN :categoryIds)
AND (LOWER(tb_product.name) LIKE LOWER(CONCAT('%',:name,'%')))
ORDER BY tb_product.name
""")
Page<ProductProjection> searchProducts(List<Long> categoryIds, String name, Pageable pageable);
@Query("SELECT obj FROM Product obj JOIN FETCH obj.categories "
+ "WHERE obj.id IN :productIds ORDER BY obj.name")
List<Product> searchProductWithCategories(List<Long> productIds);
}
Service
@Transactional(readOnly = true)
public Page<ProductDTO> findAllPaged(String name, String categoryId, Pageable pageable) {
//instanciando uma lista vazia de categoryId
List<Long> categoryIds = Arrays.asList();
//caso essa lista não tenha "0" (aquele parâmetro que passamos no controller),
//iremos separar os números, e convertê-los para uma lista de Long
if (!"0".equals(categoryIds)) {
categoryIds = Arrays.asList(categoryId.split(",")).stream().map(Long::parseLong).toList();
}
//instanciaremos uma Page do tipo Projection, realizando a primeira consulta feita (em sql)
Page<ProductProjection> page = repository.searchProducts(categoryIds, name, pageable);
//pega a page acima, e mapeia ela para uma Lista do tipo Long (para inserirmos no segundo método do repository
//(que fizemos em JPQL)
List<Long> productIds = page.map(x -> x.getId()).toList();
/* agora, criamos uma lista do tipo Product e utilizamos o método criado do repository (jpql)
* visto que ele recebe como parâmetro uma lista de Long*/
List<Product> entity = repository.searchProductWithCategories(productIds);
//reconvertendo a lista do tipo Produto para uma do tipo DTO
List<ProductDTO> dtos = entity.stream().map(x -> new ProductDTO(x, x.getCategories())).toList();
//Agora, como não é para retornar uma lista e sim Page, instanciaremos uma passando: lista de dto, o get
//pageable e o totalElements.
Page<ProductDTO> pageDTO = new PageImpl<>(dtos, page.getPageable(), page.getTotalElements());
return pageDTO;
}
Controller
@GetMapping
public ResponseEntity<Page<ProductDTO>> findAll(
@RequestParam(value = "name", defaultValue = "") String name,
@RequestParam(value = "categoryId", defaultValue = "0") String categoryId,
Pageable pageable) {
Page<ProductDTO> list = service.findAllPaged(name, categoryId, pageable);
return ResponseEntity.ok().body(list);
}
Atualizado