springjackson通过id对现有对象进行反序列化

b4lqfgs4  于 2021-07-13  发布在  Java
关注(0)|答案(2)|浏览(419)

我有以下三个实体类:
发货实体:

@Entity
@Table(name = "SHIPMENT")
public class Shipment implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "SHIPMENT_ID", nullable = false)
    private int shipmentId;

    @Column(name = "DESTINATION", nullable = false)
    private String destination;

    @OneToMany(mappedBy = "shipment")
    private List<ShipmentDetail> shipmentDetailList;

//bunch of other variables omitted

    public Shipment(String destination) {
        this.destination = destination;
        shipmentDetailList = new ArrayList<>();
    }

发货明细实体:

@Entity
@Table(name = "SHIPMENT_DETAIL")
public class ShipmentDetail implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "SHIPMENT_DETAIL_ID", nullable = false)
    private int shipmentDetailId;

    @ManyToOne
    @JoinColumn(name = "PRODUCT_ID", nullable = false)
    private Product product;

    @JsonIgnore
    @ManyToOne
    @JoinColumn(name = "SHIPMENT_ID", nullable = false)
    private Shipment shipment;

//bunch of other variables omitted 

    public ShipmentDetail() {
    }

    public ShipmentDetail(Shipment shipment, Product product) {
        this.product = product;
        this.shipment = shipment;
    }

产品实体:

@Entity
@Table(name = "Product")
public class Product implements Serializable {

    @Id
    @Column(name = "PRODUCT_ID", nullable = false)
    private String productId;

    @Column(name = "PRODUCT_NAME", nullable = false)
    private String productName;

//bunch of other variables omitted 

    public Product() {
    }

    public Product(String productId, String productName) {
        this.productId = productId;
        this.productName = productName;
    }

我通过restapi接收json。问题是我不知道如何用shipmentdetails反序列化一个新的发货,这个发货与已经存在的对象只有id的关系。我知道你可以简单地用objectmapper反序列化,但是这要求每个shipmentdetails中都有product的所有字段。如何仅用productid示例化?
收到json示例

{
    "destination": "sample Dest",
    "shipmentDetails": [
        {
            "productId": "F111111111111111"
        },
        {
            "productId": "F222222222222222"
        }
    ]
}

当前,我的rest端点将接收json,并执行以下操作:

public ResponseEntity<String> test(@RequestBody String jsonString) throws JsonProcessingException {
        JsonNode node = objectMapper.readTree(jsonString);
        String destination = node.get("destination").asText();
        Shipment newShipment = new Shipment(destination);
        shipmentRepository.save(newShipment);

        JsonNode shipmentDetailsArray = node.get("shipmentDetails");
        int shipmentDetailsArrayLength = shipmentDetailsArray.size();
        for (int c = 0; c < shipmentDetailsArrayLength; c++) {
            String productId = node.get("productId").asText();
            Product product = productRepository.findById(productId).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "No product with ID of: " + productId + " exists!"));
            ShipmentDetail shipmentDetail = new ShipmentDetail(newShipment, product, quantity);
            shipmentDetailRepository.save(shipmentDetail);
        }
    }

我想做的是:

public ResponseEntity<String> test2(@RequestBody String jsonString) throws JsonProcessingException {

    JsonNode wholeJson = objectMapper.readTree(jsonString);
    Shipment newShipment = objectMapper.treeToValue(wholeJson, Shipment.class);

    return new ResponseEntity<>("Transfer Shipment successfully created", HttpStatus.OK);
}

我尝试了这个解决方案,但没有效果:用jackson引用现有对象反序列化
如何使产品实体搜索现有产品,而不是尝试创建新产品。我一直使用的一种非常低效的解决方法是遍历json数组,使用productrepository为每个productid找到产品,然后用产品逐个设置shipmentdetail。我不确定这是否是最好的做法,因为我自学 Spring 。
所以在伪代码中我想做的是:
接收json
示例化发货实体
为每个shipmentdetail示例化shipmentdetail实体的数组:1。查找具有给定productid 2的产品。用产品和装运示例化shipmentdetail
代码已经大大简化,以更好地展示问题,

tct7dpnv

tct7dpnv1#

在这一部分中,您的代码有一个瓶颈:

Product product = productRepository.findById(productId)

因为您要对每个productid进行查询,而且它在处理大量产品时的性能会很差。忽略这一点,我将推荐这种方法。
构建您自己的反序列化程序(请参见以下内容):

public class ShipmentDeserializer extends JsonDeserializer {
     @Override
     public Shipment deserialize(JsonParser jp, DeserializationContext ctxt)
             throws IOException, JsonProcessingException {
         JsonNode node = jp.getCodec().readTree(jp);
         String destination = node.get("destination").asText();
         Shipment shipment = new Shipment(destination);
         JsonNode shipmentDetailsNode = node.get("shipmentDetails");
         List shipmentDetailList = new ArrayList();
         for (int c = 0; c < shipmentDetailsNode.size(); c++) {
             JsonNode productNode = shipmentDetailsNode.get(c);
             String productId = productNode.get("productId").asText();
             Product product = new Product(productId);
             ShipmentDetail shipmentDetail = new ShipmentDetail(product);
             shipmentDetailList.add(shipmentDetail);
         }
         shipment.setShipmentDetailList(shipmentDetailList);
         return shipment;
     }
 }

将反序列化程序添加到装运类:

@JsonDeserialize(using = ShipmentDeserializer .class)
 public class Shipment {
     // Class code
 }

反序列化字符串:

public ResponseEntity test2(@RequestBody String jsonString) throws JsonProcessingException {
     Shipment newShipment = objectMapper.readValue(jsonString, Shipment.class);
     /* More code */
     return new ResponseEntity("Transfer Shipment successfully created", HttpStatus.OK);
 }

此时,您只需要将json转换为类,因此我们需要持久化数据。

public ResponseEntity test2(@RequestBody String jsonString) throws JsonProcessingException {
     Shipment newShipment = objectMapper.readValue(jsonString, Shipment.class);
     shipmentRepository.save(newShipment);
     List<ShipmentDetail> shipmentDetails = newShipment.getShipmentDetailList();
     for (int i = 0; i < shipmentDetails.size(); c++) {
         ShipmentDetail shipmentDetail = shipmentDetails.get(i);
         shipmentDetail.setShipment(newShipment);
         Product product = productRepository.findById(productId).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "No product with ID of: " + productId + " exists!"));
         shipmentDetail.setProduct(product);
         shipmentDetailRepository.save(shipmentDetail);
     }
     return new ResponseEntity("Transfer Shipment successfully created", HttpStatus.OK);
 }

我知道您希望减少测试方法中的代码,但我不建议将json反序列化与持久层结合起来。但是如果您希望遵循该路径,可以将productrepository.findbyid(productid)移动到shipmentdeserializer类中,如下所示:

public class ShipmentDeserializer extends JsonDeserializer {
        @Override
        public Shipment deserialize(JsonParser jp, DeserializationContext ctxt)
                throws IOException, JsonProcessingException {
            JsonNode node = jp.getCodec().readTree(jp);
            String destination = node.get("destination").asText();
            Shipment shipment = new Shipment(destination);
            JsonNode shipmentDetailsNode = node.get("shipmentDetails");
            List shipmentDetailList = new ArrayList();
            for (int c = 0; c < shipmentDetailsNode.size(); c++) {
                JsonNode productNode = shipmentDetailsNode.get(c);
                String productId = productNode.get("productId").asText();
                Product product = productRepository.findById(productId).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "No product with ID of: " + productId + " exists!"));
                ShipmentDetail shipmentDetail = new ShipmentDetail(product);
                shipmentDetailList.add(shipmentDetail);
            }
            shipment.setShipmentDetailList(shipmentDetailList);
            return shipment;
        }
    }

但是如果您想这样做,您需要将存储库注入到反序列化程序中(请参见此图)。

new9mtju

new9mtju2#

我认为你目前的方法不是一个坏的解决办法,你正在正确地处理问题,而且只需几步。
不管怎样,你可以尝试以下方法。
这个想法将提供一个新的领域, productId ,在支持与的关系的同一数据库列上定义 Product 实体,类似于:

@Entity
@Table(name = "SHIPMENT_DETAIL")
public class ShipmentDetail implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "SHIPMENT_DETAIL_ID", nullable = false)
    private int shipmentDetailId;

    @Column(name = "PRODUCT_ID", nullable = false)
    private String productId;

    @ManyToOne
    @JoinColumn(name = "PRODUCT_ID", insertable = false, updatable = false)
    private Product product;

    @JsonIgnore
    @ManyToOne
    @JoinColumn(name = "SHIPMENT_ID", nullable = false)
    private Shipment shipment;

//bunch of other variables omitted 

    public ShipmentDetail() {
    }

    public ShipmentDetail(Shipment shipment, Product product) {
        this.product = product;
        this.shipment = shipment;
    }
}

这个 product 字段必须注解为not insertable 而不是 updatable :相反,hibernate会抱怨应该使用哪个字段来维护与的关系 Product 实体,换句话说,保持实际的列值。
修改 Shipment 与…的关系 ShipmentDetail 以及传播持久性操作(根据需要调整代码):

@OneToMany(mappedBy = "shipment", cascade = CascadeType.ALL, orphanRemoval = true)
private List<ShipmentDetail> shipmentDetailList;

然后,您可以安全地依赖spring+jackson反序列化并获得对接收到的 Shipment 对象:

public ResponseEntity<String> processShipment(@RequestBody Shipment shipment) {
  // At this point shipment should contain the different details,
  // each with the corresponding productId information

  // Perform the validations required, log information, if necessary

  // Save the object: it should persist the whole object tree in the database
  shipmentRepository.save(shipment);
}

这种方法有一个明显的缺点,即 Product 没有事先检查。
尽管您可以使用外键确保数据库级别的数据完整性,但在执行实际插入之前验证信息是否正确可能会很方便:

public ResponseEntity<String> processShipment(@RequestBody Shipment shipment) {
  // At this point shipment should contain the different details,
  // each with the corresponding productId information

  // Perform the validations required, log information, if necessary
  List<ShipmentDetail> shipmentDetails = shipment.getShipmentDetails();
  if (shipmentDetails == null || shipmentDetails.isEmpty()) {
    // handle error as appropriate
    throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "No shipment details provided");
  }

  shipmentDetails.forEach(shipmentDetail -> {
    String productId = shipmentDetail.getProductId();
    Product product = productRepository.findById(productId).orElseThrow(
      () -> new ResponseStatusException(HttpStatus.NOT_FOUND, 
            "No product with ID of: " + productId + " exists!")
    )
  });

  // Everything looks fine, save the object now
  shipmentRepository.save(shipment);
}

相关问题