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

Spring Boot中如何在POST请求JSON仅传递manufacturerId外键?

问题描述

我正在开发一个在线鞋店,为商品模块创建REST控制器,当前测试POST请求时,商品与厂商通过manufacturerId关联(商品表goods通过ManufacturerId字段关联厂商表manufacturers的id字段)。目前我需要在JSON中传入厂商的完整信息,但认为这并非最佳实践。

商品实体类

@Entity
@Table(name = "goods")
public class Good {
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    //@JsonIgnore
    private int id;

    @Column(name = "Title")
    private String title;

//    @Enumerated(EnumType.STRING)
//    @Column(name = "sex")
//    private sexType sexType;

    @Column(name = "sex")
    private String sex;

    /**
     * proved and it actually works!
     */
    //@JsonManagedReference
    @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.DETACH,
    CascadeType.REFRESH, CascadeType.MERGE})
    @JoinColumn(name = "ManufacturerId")
    //@JsonIgnoreProperties
    Manufacturer manufacturer; //Many goods can refer to one manufacturer


    @JsonManagedReference
    @OneToMany(cascade = {CascadeType.PERSIST, CascadeType.DETACH,
            CascadeType.REFRESH, CascadeType.MERGE}, mappedBy = "good")//Uni-directional reference to size array
    List<Size> sizes;
}

厂商实体类

@Entity
@Table(name = "manufacturers")
public class Manufacturer {
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @Column(name = "ManufacturerName")
    private String manufacturerName;

    @Column(name = "Address")
    private String address;

    @Column(name = "Telephone")
    private String telephone;

    //@JsonBackReference
    @OneToMany(cascade = {CascadeType.PERSIST, CascadeType.DETACH,
            CascadeType.REFRESH, CascadeType.MERGE},
            mappedBy = "manufacturer")
    @JsonIgnore
    private List<Good> goods;
}

当前POST请求JSON示例

{
  "title": "Model",
  "sex": "CHILD",
  "manufacturer": {
    "id": 1,
    "manufacturerName": "Ralf",
    "address": "Moscow",
    "telephone": "9-999-999-99-99"
  },
  "sizes": [
    {
      "size": 37,
      "quantity": 5
    },
    {
      "size": 39,
      "quantity": 4
    }
  ]
}

疑问

  1. 如何仅传递manufacturerId来创建商品?
  2. 担心使用@JsonIgnore注解后,无法获取厂商的额外信息,该怎么处理?

解决方案

一、仅传递manufacturerId创建商品

推荐使用**DTO(数据传输对象)**方案,这是REST接口开发的最佳实践,能清晰区分输入输出数据结构,避免直接暴露实体类。

步骤1:创建输入DTO

定义专门用于接收创建请求的DTO,只保留必要字段,厂商仅用manufacturerId表示:

public class GoodCreateDTO {
    private String title;
    private String sex;
    private Integer manufacturerId;
    private List<SizeCreateDTO> sizes;

    // Getter、Setter方法
}

public class SizeCreateDTO {
    private Integer size;
    private Integer quantity;

    // Getter、Setter方法
}

步骤2:控制器中处理DTO转换

在REST控制器接收DTO,手动转换为实体类并完成关联查询:

@PostMapping("/goods")
public ResponseEntity<GoodResponseDTO> createGood(@RequestBody GoodCreateDTO dto) {
    // 从数据库查询对应ID的厂商,不存在则抛出异常
    Manufacturer manufacturer = manufacturerRepository.findById(dto.getManufacturerId())
            .orElseThrow(() -> new RuntimeException("指定ID的厂商不存在"));
    
    // 转换为Good实体
    Good good = new Good();
    good.setTitle(dto.getTitle());
    good.setSex(dto.getSex());
    good.setManufacturer(manufacturer);
    
    // 处理尺码列表转换
    List<Size> sizes = dto.getSizes().stream()
            .map(sizeDto -> {
                Size size = new Size();
                size.setSize(sizeDto.getSize());
                size.setQuantity(sizeDto.getQuantity());
                size.setGood(good);
                return size;
            })
            .collect(Collectors.toList());
    good.setSizes(sizes);
    
    // 保存商品
    Good savedGood = goodRepository.save(good);
    
    // 转换为响应DTO返回(下文会说明)
    return ResponseEntity.ok(convertToResponseDTO(savedGood));
}

简化后的POST请求JSON

{
  "title": "Model",
  "sex": "CHILD",
  "manufacturerId": 1,
  "sizes": [
    {
      "size": 37,
      "quantity": 5
    },
    {
      "size": 39,
      "quantity": 4
    }
  ]
}

二、解决@JsonIgnore导致无法获取厂商信息的问题

当前你在Manufacturer的goods字段上加@JsonIgnore,只会忽略厂商关联的商品列表,不会影响厂商自身信息的序列化。如果是担心返回商品时无法带出厂商信息,可通过以下方式处理:

方案1:使用响应DTO返回完整信息

创建专门的响应DTO,包含商品和厂商的完整信息:

public class GoodResponseDTO {
    private Integer id;
    private String title;
    private String sex;
    private ManufacturerResponseDTO manufacturer;
    private List<SizeResponseDTO> sizes;

    // Getter、Setter方法
}

public class ManufacturerResponseDTO {
    private Integer id;
    private String manufacturerName;
    private String address;
    private String telephone;

    // Getter、Setter方法
}

public class SizeResponseDTO {
    private Integer size;
    private Integer quantity;

    // Getter、Setter方法
}

在控制器中实现实体到DTO的转换(可使用MapStruct等工具简化转换逻辑),返回时就能展示完整的厂商信息,同时避免实体循环引用问题。

方案2:调整实体序列化注解

如果不想用DTO,可调整实体类的注解,确保序列化时能返回厂商信息:

  • 保留Manufacturer类的所有字段,goods字段继续用@JsonIgnore避免循环引用;
  • Good类的manufacturer字段不要加@JsonIgnore,这样返回商品时会自动带出关联的厂商完整信息。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 18:43:11