Spring Boot使用UserInfoDto转Users实体存入数据库报错排查
修复Spring Boot注册功能的DataIntegrityViolationException问题
问题根源
报错Users.address.cityName非空属性引用null值,本质是JPA实体的非空约束字段被赋值为null,触发数据库级别的非空校验失败。常见触发场景:
- 前端表单未正确传递
cityName字段,DTO接收后该值为null - DTO转
Users实体时,address对象未提前实例化,直接赋值属性导致null传递 - 实体类
Address的cityName标注了非空约束,但业务逻辑没做前置校验
具体修复步骤
1. 核对前端表单字段映射
确保前端HTML表单的cityName输入框name属性和DTO的字段层级完全匹配,比如:
<form method="post" action="/register"> <input type="text" name="account" placeholder="账号" required> <input type="password" name="password" placeholder="密码" required> <!-- 嵌套字段要写全层级 --> <input type="text" name="address.cityName" placeholder="城市名称" required> <input type="text" name="address.provinceName" placeholder="省份" required> </form>
如果DTO里是嵌套的AddressDto,表单字段必须用address.cityName这种格式,Spring才能自动完成参数绑定。
2. 完善DTO到实体的转换逻辑
转换时必须先实例化Address对象,避免直接赋值null属性。示例代码:
// UserInfoDto 示例 public class UserInfoDto { private String account; private String password; private AddressDto address; // getter/setter } // AddressDto 示例 public class AddressDto { private String cityName; private String provinceName; // getter/setter } // 转换方法示例 public Users convertToEntity(UserInfoDto dto) { Users user = new Users(); user.setAccount(dto.getAccount()); user.setPassword(dto.getPassword()); // 注意:实际业务要加密存储 // 关键:先实例化Address,再赋值属性 Address address = new Address(); address.setCityName(dto.getAddress().getCityName()); address.setProvinceName(dto.getAddress().getProvinceName()); user.setAddress(address); return user; }
如果DTO的address可能为null,要加非空判断避免空指针:
if (dto.getAddress() == null || StringUtils.isBlank(dto.getAddress().getCityName())) { throw new IllegalArgumentException("城市名称不能为空"); }
3. 对齐实体类与数据库约束
检查Address实体类的cityName注解,确保和数据库表的约束一致:
@Entity public class Address { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @NotNull(message = "城市名称不能为空") // JPA校验注解 @Column(nullable = false) // 对应数据库字段NOT NULL约束 private String cityName; // 其他字段、getter/setter }
如果业务允许cityName为空,需要同时修改实体类注解(去掉@NotNull和nullable=false)和数据库表结构(移除NOT NULL约束)。
4. 增加前置参数校验
在控制器层添加参数校验,提前拦截非法请求,避免到数据库层报错:
@RestController @RequestMapping("/user") public class UserController { @PostMapping("/register") public ResponseEntity<String> register(@Valid @RequestBody UserInfoDto dto, BindingResult result) { // 校验参数合法性 if (result.hasErrors()) { String errorMsg = result.getFieldErrors().stream() .map(FieldError::getDefaultMessage) .collect(Collectors.joining(",")); return ResponseEntity.badRequest().body(errorMsg); } // 后续转换实体、保存逻辑 userService.saveUser(dto); return ResponseEntity.ok("注册成功"); } }
同时给DTO的cityName添加校验注解:
public class AddressDto { @NotBlank(message = "城市名称不能为空") private String cityName; // 其他字段 }
5. 排查数据接收情况
如果以上步骤没解决,打印DTO接收的完整数据,确认是否真的拿到了cityName:
// 用日志或控制台打印接收的DTO log.info("接收的注册DTO:{}", dto); // 或直接控制台输出 System.out.println("DTO数据:" + dto.toString());
如果打印发现cityName为null,说明前端未正确传递,需检查表单提交方式、字段名拼写、输入框是否被禁用等。
内容的提问来源于stack exchange,提问作者참직김
相关产品推荐
相关产品推荐

