Skip to content

Commit c8d7b7f

Browse files
pathakharshitclaude
andcommitted
fix(mysql-crud): use generated keys instead of LAST_INSERT_ID(), reject orders for missing users
LAST_INSERT_ID() is connection-scoped; a pooled JdbcTemplate follow-up call can land on a different physical connection than the INSERT and return the wrong id (or null). Use KeyHolder to read the generated key from the same statement/connection as the insert. createOrder also inserted into orders even when the target user did not exist, creating orphaned rows (no FK constraint) and a misleading 200 response. Now returns 404 before inserting. Verified locally end-to-end against a real mysql:8.0 container: createUser returns the correct row for its generated id, createOrder returns the correct order for its generated id, and a 999 (nonexistent user) orders request 404s with zero rows written. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 826d633 commit c8d7b7f

1 file changed

Lines changed: 39 additions & 7 deletions

File tree

mysql-crud/src/main/java/com/keploy/sample/ApiController.java

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
package com.keploy.sample;
22

3+
import org.springframework.http.HttpStatus;
34
import org.springframework.jdbc.core.JdbcTemplate;
5+
import org.springframework.jdbc.support.GeneratedKeyHolder;
6+
import org.springframework.jdbc.support.KeyHolder;
47
import org.springframework.web.bind.annotation.*;
8+
import org.springframework.web.server.ResponseStatusException;
59

610
import java.math.BigDecimal;
11+
import java.sql.PreparedStatement;
12+
import java.sql.Statement;
713
import java.util.LinkedHashMap;
814
import java.util.List;
915
import java.util.Map;
@@ -60,8 +66,20 @@ public Map<String, Object> getUser(@PathVariable long id) {
6066
public Map<String, Object> createUser(@RequestBody Map<String, Object> body) {
6167
String name = String.valueOf(body.getOrDefault("name", "unknown"));
6268
String email = String.valueOf(body.getOrDefault("email", "unknown@example.com"));
63-
jdbc.update("INSERT INTO users(name,email) VALUES(?,?)", name, email);
64-
Long id = jdbc.queryForObject("SELECT LAST_INSERT_ID()", Long.class);
69+
70+
// LAST_INSERT_ID() is connection-scoped; a pooled JdbcTemplate call can
71+
// land on a different physical connection than the INSERT. Use
72+
// generated keys from the same statement/connection instead.
73+
KeyHolder keyHolder = new GeneratedKeyHolder();
74+
jdbc.update(connection -> {
75+
PreparedStatement ps = connection.prepareStatement(
76+
"INSERT INTO users(name,email) VALUES(?,?)", Statement.RETURN_GENERATED_KEYS);
77+
ps.setString(1, name);
78+
ps.setString(2, email);
79+
return ps;
80+
}, keyHolder);
81+
long id = keyHolder.getKey().longValue();
82+
6583
jdbc.update("INSERT INTO audit_log(action,detail) VALUES(?,?)", "create_user", "name=" + name);
6684
Integer userCount = jdbc.queryForObject("SELECT COUNT(*) FROM users", Integer.class);
6785
Map<String, Object> created = jdbc.queryForMap("SELECT id,name,email FROM users WHERE id=?", id);
@@ -73,16 +91,30 @@ public Map<String, Object> createUser(@RequestBody Map<String, Object> body) {
7391

7492
@PostMapping("/users/{id}/orders")
7593
public Map<String, Object> createOrder(@PathVariable long id, @RequestBody Map<String, Object> body) {
76-
Object amount = body.getOrDefault("amount", 0);
77-
String status = String.valueOf(body.getOrDefault("status", "PENDING"));
7894
Integer userExists = jdbc.queryForObject("SELECT COUNT(*) FROM users WHERE id=?", Integer.class, id);
79-
jdbc.update("INSERT INTO orders(user_id,amount,status) VALUES(?,?,?)", id, amount, status);
80-
Long orderId = jdbc.queryForObject("SELECT LAST_INSERT_ID()", Long.class);
95+
if (userExists == null || userExists == 0) {
96+
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "user " + id + " does not exist");
97+
}
98+
99+
BigDecimal amount = new BigDecimal(String.valueOf(body.getOrDefault("amount", 0)));
100+
String status = String.valueOf(body.getOrDefault("status", "PENDING"));
101+
102+
KeyHolder keyHolder = new GeneratedKeyHolder();
103+
jdbc.update(connection -> {
104+
PreparedStatement ps = connection.prepareStatement(
105+
"INSERT INTO orders(user_id,amount,status) VALUES(?,?,?)", Statement.RETURN_GENERATED_KEYS);
106+
ps.setLong(1, id);
107+
ps.setBigDecimal(2, amount);
108+
ps.setString(3, status);
109+
return ps;
110+
}, keyHolder);
111+
long orderId = keyHolder.getKey().longValue();
112+
81113
Map<String, Object> order = jdbc.queryForMap("SELECT id,user_id,amount,status FROM orders WHERE id=?", orderId);
82114
Integer orderCount = jdbc.queryForObject("SELECT COUNT(*) FROM orders WHERE user_id=?", Integer.class, id);
83115
jdbc.update("INSERT INTO audit_log(action,detail) VALUES(?,?)", "create_order", "user=" + id);
84116
Map<String, Object> r = new LinkedHashMap<>();
85-
r.put("userExists", userExists != null && userExists > 0);
117+
r.put("userExists", true);
86118
r.put("order", order);
87119
r.put("orderCountForUser", orderCount);
88120
return r;

0 commit comments

Comments
 (0)