You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Servlet+JSP向带自增外键的MySQL表插入数据

解决Servlet中使用905910关联插入两张表的问题

你现在的核心问题是没有正确使用JDBC的PreparedStatement,同时忽略了事务的原子性要求,还有几个细节错误导致905910无法生效。咱们一步步梳理并修正:

先指出你代码里的关键问题:

  1. PreparedStatement执行方式错误:你调用了ce.executeUpdate(insertSQL),这会忽略之前用setString/setInt绑定的参数,直接执行传入的静态SQL,导致第一个插入操作根本没用到表单数据,自然905910也拿不到正确的ID。应该使用无参的ce.executeUpdate()。
  2. 未启用事务:两个插入操作是强关联的,如果第一个成功第二个失败,会导致数据不一致。必须用事务保证两个操作要么都成功,要么都失败。
  3. 静态Connection线程不安全:Servlet是多线程环境,静态Connection会被多个请求共享,引发并发问题,每次请求应该获取独立的数据库连接。
  4. HTML中event_id输入框多余:前端传905910是无效的,这是SQL函数,前端只能传字符串;而且这个ID应该从数据库自动获取,不需要用户输入。
  5. 日期格式不匹配:前端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();
            }
        }
    }
}

关键要点解释:

  1. 905910的作用范围:它是基于当前数据库连接的,只要两个插入操作使用同一个Connection,就能正确获取到刚插入的event的ID。
  2. 事务的原子性:通过conn.setAutoCommit(false)关闭自动提交,操作全部完成后commit(),异常时rollback(),确保数据一致性。
  3. 日期格式转换:把前端传入的T替换为空格,匹配MySQL的datetime格式要求。
  4. 资源管理:在finally块中关闭Statement和Connection,避免数据库连接泄漏。

内容的提问来源于stack exchange,提问作者mariad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:24:57