Save the admin price grid in one request

Java · Spring Boot · intermediate · modification

Adds the bulk save behind the admin price grid, so a merchandiser can change many prices in one click instead of editing products one by one. `BulkPriceController` validates the envelope and hands the rows to `BulkPriceService.apply`, which runs in one transaction. A first pass skips the grid's blank rows and rejects repeated SKUs and bad prices with the row number the admin sees. One `findBySkuIn` query then loads every product the request names, so an unknown SKU is rejected before anything changes. The last pass sets the prices and Hibernate flushes them on commit. Row errors come back as a `ProblemDetail` with the row number in `detail`.

Shop backend on Spring Boot 3.3 (Java 21), Spring Data JPA (Hibernate 6) on PostgreSQL. Existing and unchanged: the `Product` entity (`id`; `sku`, unique, non-null, `varchar(32)`; `name`; `price`, `numeric(7,2) NOT NULL`) with `setPrice`, and `ProductRepository extends JpaRepository<Product, Long>` with `findBySku`. The existing `SecurityConfig` restricts `/api/admin/**` to `ROLE_ADMIN` and handles CSRF for the admin console. Jackson binds `price` to `BigDecimal` and accepts a JSON number or a numeric string. Bean Validation failures on a `@Valid` request body are answered with 400 by Spring MVC's default handling. Two admins saving prices at the same moment is last-write-wins by product decision; the grid reloads after every save.

Requirements

Files touched

--- src/main/java/com/acme/shop/catalog/ProductRepository.java
 
 import org.springframework.data.jpa.repository.JpaRepository;
+import java.util.Collection;
+import java.util.List;
 import java.util.Optional;
 
 
     Optional<Product> findBySku(String sku);
+
+    List<Product> findBySkuIn(Collection<String> skus);
 }
 
--- src/main/java/com/acme/shop/catalog/BulkPriceRequest.java
+package com.acme.shop.catalog;
+
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import java.math.BigDecimal;
+import java.util.List;
+
+public record BulkPriceRequest(@NotNull @Size(max = 500) List<@NotNull Row> rows) {
+
+    public record Row(String sku, BigDecimal price) {
+    }
+}
+
--- src/main/java/com/acme/shop/catalog/BulkPriceService.java
+package com.acme.shop.catalog;
+

Review this PR

Java practice