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

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,提问作者참직김

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 00:20:28