Measure first: the old GET /customers/1/orders ran one query for the customer's orders, then one more per order for that order's items. Customer 1 has 2 orders, so that was 3 queries; a customer with 50 orders would cost 51. This is the N+1 problem, and it hides in any loop that runs a query.
A JOIN brings rows of two tables together in one query. For example, a route showing one order's items could get each item's quantity with its product's name in a single query:
String query = "SELECT order_items.quantity, products.name ";
query += "FROM order_items ";
query += "JOIN products ";
query += "ON products.id = order_items.product_id ";
query += "WHERE order_items.order_id = ? ";
List<Map<String, Object>> rows = runQuery(query, orderId);
Each += glues one more piece onto the end of the string, so each piece ends with a space. Each row of the orders JOIN is one item, with its order's columns next to it, so an order with two items comes as two rows. A HashMap from order id to its list of items groups them back into orders.
Your code must give the same answers as before, with one query per request:
GET /customers/1/orders → 200 [{"id": 1, "items": [{"product_id": 1, "quantity": 2}, {"product_id": 4, "quantity": 5}], "ordered": "2026-03-02", "status": "paid"}, {"id": 3, "items": [{"product_id": 2, "quantity": 3}, {"product_id": 4, "quantity": 10}], "ordered": "2026-04-05", "status": "paid"}]
GET /debug/queries → 200 {"queries": 1}
GET /customers/3/orders → 200 [{"id": 4, "items": [{"product_id": 5, "quantity": 1}, {"product_id": 1, "quantity": 1}], "ordered": "2026-04-18", "status": "shipped"}, {"id": 7, "items": [{"product_id": 1, "quantity": 4}, {"product_id": 2, "quantity": 2}], "ordered": "2026-05-30", "status": "paid"}]
GET /debug/queries → 200 {"queries": 2}
GET /customers/4/orders → 200 [{"id": 5, "items": [{"product_id": 6, "quantity": 2}, {"product_id": 3, "quantity": 1}], "ordered": "2026-05-07", "status": "paid"}]
GET /customers/5/orders → 200 []
GET /debug/queries → 200 {"queries": 4}
Your task: in getCustomerOrders, build one query that joins orders and order_items for this customer, sorted by order id. Run it once, then group its rows into orders with a HashMap entries from each order id to its list of items.
Assignment
Follow the Coding Tutorial to replace the query-per-order loop with one JOIN and group its rows in Java.
Hint
An order with two items comes as two rows with the same row.get("id"). Only when that id isn't in entries yet, put a new empty ArrayList there and add the order to result with that same list as "items". Every row then adds its item to entries.get(row.get("id")).
Free plan: 30 tutor messages. PRO is unlimited.
