如何通过Servlet+JSP向带自增外键的MySQL表插入数据
解决Servlet中使用905910关联插入两张表的问题
你现在的核心问题是没有正确使用JDBC的PreparedStatement,同时忽略了事务的原子性要求,还有几个细节错误导致905910无法生效。咱们一步步梳理并修正:
先指出你代码里的关键问题:
- PreparedStatement执行方式错误:你调用了
ce.executeUpdate(insertSQL),这会忽略之前用setString/setInt绑定的参数,直接执行传入的静态SQL,导致第一个插入操作根本没用到表单数据,自然905910也拿不到正确的ID。应该使用无参的ce.executeUpdate()。 - 未启用事务:两个插入操作是强关联的,如果第一个成功第二个失败,会导致数据不一致。必须用事务保证两个操作要么都成功,要么都失败。
- 静态Connection线程不安全:Servlet是多线程环境,静态Connection会被多个请求共享,引发并发问题,每次请求应该获取独立的数据库连接。
- HTML中event_id输入框多余:前端传
905910是无效的,这是SQL函数,前端只能传字符串;而且这个ID应该从数据库自动获取,不需要用户输入。 - 日期格式不匹配:前端
datetime-local组件返回的格式是yyyy-MM-ddTHH:mm:ss(比如2018-02-02T20:00:00),而MySQL的datetime类型需要yyyy-MM-dd HH:mm:ss格式,直接插入会报错。
修正后的HTML代码(移除无用的event_id输入框):
<section> <form name="create" action="${pageContext.request.contextPath}/createEventShift" method="post"> <hr> <label for="title"><b>Event Name</b></label> <input type="text" placeholder="Enter title of the event" name="title" required> <hr> <label for="description"><b>Description</b></label> <input type="text" placeholder="Describe your event" name="description" required> <label for="guest_no"><b>Number of Guests</b></label> <input type="number" placeholder="Write how many guests" name="guest_no" required> <label for="start"><b>Start Date & Time</b></label> <input type="datetime-local" placeholder="Start" name="start" step="2" required> <label for="end"><b>End Date & Time</b></label> <input type="datetime-local" placeholder="End" name="end" step="2" required> <div class="expansive-button v2"> <div><i></i></div> </div> <h3>Add Shift</h3> <label for="startshift"><b>Shift Start</b></label> <input type="datetime-local" placeholder="Start date and time of shift" name="startshift" step="2" required> <label for="endshift"><b>Shift End</b></label> <input type="datetime-local" placeholder="End date and time of shift" name="endshift" step="2" required> <label for="positions"><b>Number of Staff Needed</b></label> <input type="number" placeholder="How many staff do you need for this shift" name="positions" required> <br/><br /> <button type="submit">Submit</button> </form> </section>
修正后的Servlet代码:
@WebServlet("/createEventShift") public class CreateEventShift extends HttpServlet { // 类名首字母大写,符合Java编码规范 private static final String URL = "jdbc:mysql://localhost:3306/e_manager?useSSL=false&serverTimezone=UTC"; private static final String USER = "root"; private static final String PASSWORD = "2timeLearning!"; @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置请求编码,避免中文乱码 request.setCharacterEncoding("UTF-8"); // 获取表单参数并转换日期格式 String title = request.getParameter("title"); String description = request.getParameter("description"); String eventStart = request.getParameter("start").replace("T", " "); String eventEnd = request.getParameter("end").replace("T", " "); int guestNo = Integer.parseInt(request.getParameter("guest_no")); String shiftStart = request.getParameter("startshift").replace("T", " "); String shiftEnd = request.getParameter("endshift").replace("T", " "); int positions = Integer.parseInt(request.getParameter("positions")); Connection conn = null; PreparedStatement eventStmt = null; PreparedStatement shiftStmt = null; try { // MySQL 8.0+ 可省略驱动加载,会自动注册 Class.forName("com.mysql.cj.jdbc.Driver"); conn = DriverManager.getConnection(URL, USER, PASSWORD); // 开启事务:关闭自动提交,手动控制事务边界 conn.setAutoCommit(false); // 插入event表 String insertEventSQL = "INSERT INTO event(title, description, start, end, guest_no) VALUES(?, ?, ?, ?, ?)"; eventStmt = conn.prepareStatement(insertEventSQL); eventStmt.setString(1, title); eventStmt.setString(2, description); eventStmt.setString(3, eventStart); eventStmt.setString(4, eventEnd); eventStmt.setInt(5, guestNo); eventStmt.executeUpdate(); // 无参执行,使用绑定的参数 // 插入shift表:使用905910获取当前连接中刚插入的event_id String insertShiftSQL = "INSERT INTO shift(event_id, start, end, positions) VALUES(905910, ?, ?, ?)"; shiftStmt = conn.prepareStatement(insertShiftSQL); shiftStmt.setString(1, shiftStart); shiftStmt.setString(2, shiftEnd); shiftStmt.setInt(3, positions); shiftStmt.executeUpdate(); // 事务提交:所有操作成功后才提交 conn.commit(); response.sendRedirect(request.getContextPath() + "/viewEvents.jsp"); } catch (ClassNotFoundException | SQLException ex) { // 异常回滚:如果任意步骤失败,撤销所有操作 if (conn != null) { try { conn.rollback(); } catch (SQLException rollbackEx) { rollbackEx.printStackTrace(); } } ex.printStackTrace(); // 打印错误日志,方便调试 response.sendRedirect(request.getContextPath() + "/error.jsp"); // 跳转错误页面 } finally { // 按逆序关闭资源,避免泄漏 try { if (shiftStmt != null) shiftStmt.close(); if (eventStmt != null) eventStmt.close(); if (conn != null) conn.close(); } catch (SQLException ex) { ex.printStackTrace(); } } } }
关键要点解释:
- 905910的作用范围:它是基于当前数据库连接的,只要两个插入操作使用同一个Connection,就能正确获取到刚插入的event的ID。
- 事务的原子性:通过
conn.setAutoCommit(false)关闭自动提交,操作全部完成后commit(),异常时rollback(),确保数据一致性。 - 日期格式转换:把前端传入的
T替换为空格,匹配MySQL的datetime格式要求。 - 资源管理:在finally块中关闭Statement和Connection,避免数据库连接泄漏。
内容的提问来源于stack exchange,提问作者mariad
相关产品推荐
相关产品推荐

