Spring MVC中POST请求时Controller对象未填充致数据保存失败
Spring MVC表单提交后数据无法保存的问题排查
问题描述
开发Spring MVC应用时,在JSP表单点击save按钮后数据无法保存,触发如下错误:
Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null];
控制台输出显示,插入方法接收的Pass_in_trip对象大部分属性为空,仅place字段有值:
Pass_in_trip [key=null, trip=Trip [trip_no=0, comp=Company [id_comp=0, name=null], plane=null, town_from=null, town_to=null, time_out=null, time_in=null, passInTrips=[]], passenger=Passenger [name=null, passengerId=0, passInTrips=[]], place=6f, date=null]
错误原因分析
- 表单参数绑定错误:JSP中直接传递整个实体对象字符串(如
<select name="trip">),Spring无法将字符串转换为Trip/Passenger实例,只能生成空对象;日期字段type="datetime"兼容性差,无法自动转为Timestamp类型。 - 复合主键未初始化:
Pass_in_trip使用@EmbeddedId复合主键,但提交时未传递主键字段值,Spring无法自动生成复合主键,导致key为null触发数据库约束异常。 - 未使用Spring表单标签:引入了Spring form标签库但未使用
<form:form>,无法利用Spring数据绑定特性处理嵌套属性。
解决方案
1. 修改JSP表单,使用Spring Form标签修正绑定
替换普通表单为Spring form标签,下拉框传递实体主键,日期字段调整为兼容格式:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Pass_in_trip</title> <style> form fieldset { width: 40%; } form fieldset label { display: block; } form fieldset input, form fieldset select, form fieldset textarea { width: 100%; } </style> </head> <body> <h1>Pass_in_trip</h1> <form:form modelAttribute="pass_in_trip" method="POST"> <fieldset> <div> <label>Flight:</label> <form:select path="trip.trip_no"> <c:forEach var="trip" items="${trip}"> <form:option value="${trip.trip_no}" label="№ ${trip.trip_no} ${trip.town_from} ${trip.town_to}"/> </c:forEach> </form:select> </div> <div> <label>Passenger:</label> <form:select path="passenger.passengerId"> <c:forEach var="passenger" items="${passenger}"> <form:option value="${passenger.passengerId}" label="${passenger.passengerId} ${passenger.name}"/> </c:forEach> </form:select> </div> <div> <label>Date: </label> <form:input type="datetime-local" path="date" value="${pass_in_trip.date != null ? #dates.format(pass_in_trip.date, 'yyyy-MM-dd\'T\'HH:mm') : ''}"/> </div> <div> <label>Place:</label> <form:input type="text" path="place" value="${pass_in_trip.place}"/> </div> <div> <input type="submit" value="Save"> </div> </fieldset> </form:form> </body> </html>
2. 修改Controller,初始化表单对象并处理复合主键
在GET方法中初始化Pass_in_trip对象,POST方法手动设置复合主键:
@PreAuthorize("hasRole('ROLE_Admin')") @RequestMapping(value = "insert", method = RequestMethod.GET) public String insertnewform(Model uiModel) { Pass_in_trip pass_in_trip = new Pass_in_trip(); pass_in_trip.setKey(new KeysPass_in_trip()); uiModel.addAttribute("pass_in_trip", pass_in_trip); uiModel.addAttribute("trip", service.findallTrip()); uiModel.addAttribute("passenger", service.findallPassenger()); return "/pass_in_trip/insert"; } @PreAuthorize("hasRole('ROLE_Admin')") @RequestMapping(value = "insert", method = RequestMethod.POST) public String insert(@ModelAttribute("pass_in_trip") Pass_in_trip pass_in_trip, BindingResult bindingResult, Model uiModel, RedirectAttributes redirectatributes) { System.out.println(pass_in_trip); if (bindingResult.hasErrors()) { uiModel.addAttribute("trip", service.findallTrip()); uiModel.addAttribute("passenger", service.findallPassenger()); return "/pass_in_trip/insert"; } // 手动设置复合主键值 KeysPass_in_trip key = new KeysPass_in_trip(); key.setTripId(pass_in_trip.getTrip().getTrip_no()); key.setPsgId(pass_in_trip.getPassenger().getPassengerId()); pass_in_trip.setKey(key); service.save(pass_in_trip); redirectatributes.addFlashAttribute("message", "保存成功"); return "redirect:/pass_in_trip/"; }
3. 确保实体类Getter/Setter完整
Pass_in_trip、Trip、Passenger及KeysPass_in_trip的所有属性必须包含对应的Getter和Setter方法,Spring依赖这些方法完成数据绑定。
4. 日期类型转换配置(可选)
若日期绑定仍有问题,添加Spring日期转换器:
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addFormatters(FormatterRegistry registry) { registry.addConverter(new Converter<String, Timestamp>() { @Override public Timestamp convert(String source) { if (source == null || source.isEmpty()) { return null; } try { LocalDateTime localDateTime = LocalDateTime.parse(source, DateTimeFormatter.ISO_LOCAL_DATE_TIME); return Timestamp.valueOf(localDateTime); } catch (DateTimeParseException e) { throw new IllegalArgumentException("Invalid date format. Please use yyyy-MM-dd'T'HH:mm"); } } }); } }
内容的提问来源于stack exchange,提问作者Natasha
相关产品推荐
相关产品推荐

