Page through a product's reviews

Java · Spring Boot · beginner · modification

Adds the paged reviews list for the product page. Until now the page only showed the three top-rated reviews (`findTop3ByProductIdOrderByRatingDescCreatedAtDesc`); this adds `GET /api/products/{productId}/reviews` so shoppers can read all of them, ten at a time, newest first. The repository gains a `Page`-returning `findByProductId`, `ReviewService.productReviews` builds the `PageRequest` with a stable newest-first sort and maps the result into the shop's `PageView` envelope, and `ReviewController` validates `page` with `@Min(1)` / `@Max(1000)`, defaulting it to 1.

Shop backend on Spring Boot 3.3 (Java 21), Spring Data JPA (Hibernate 6) on PostgreSQL. Existing and unchanged: the `Review` entity (`id`, `productId`, `rating`, `body`, `authorName`, `createdAt`, a non-null `Instant`); the `reviews` table holds only published reviews, so every row is public. `ReviewView` is a record with `ReviewView.from(review)`. `PageView<T>(List<T> items, int page, int totalPages, long totalItems)` is the shop's shared list envelope. The security config already permits anonymous `GET /api/products/**`. With no `@Validated` on the controller, Spring MVC 6.1's built-in method validation answers a `@Min`/`@Max` violation on a `@RequestParam` with 400, and a non-numeric `page` is a 400 type mismatch. A `Page`-returning Spring Data finder applies the `Pageable`'s offset, size and sort in the query and computes the totals (with a count query when needed). `reviews` has an index on `(product_id, created_at, id)`. A product has at most a few thousand reviews.

Requirements

Files touched

--- src/main/java/com/acme/shop/reviews/ReviewRepository.java
 package com.acme.shop.reviews;
 
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
 import org.springframework.data.jpa.repository.JpaRepository;
 import java.util.List;
 
     List<Review> findTop3ByProductIdOrderByRatingDescCreatedAtDesc(Long productId);
+
+    Page<Review> findByProductId(Long productId, Pageable pageable);
 }
 
--- src/main/java/com/acme/shop/reviews/ReviewService.java
+package com.acme.shop.reviews;
+
+import com.acme.shop.web.PageView;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Sort;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.util.List;
+
+@Service
+public class ReviewService {
+
+    static final int PAGE_SIZE = 10;
+
+    private static final Sort NEWEST_FIRST = Sort.by(Sort.Order.desc("createdAt"), Sort.Order.desc("id"));

Review this PR

Java practice