user3768498
user3768498

Reputation: 41

How to send only the ID the of main nested objects in the body request in spring boot

I'm creating eCommerce for merchants using spring boot with JPA. I have an issue while creating the order service. I want to only pass the ID of the nested objects in the request body instead of sending the full nest objects because the size will be extremely big.

Here is my code. Merchant can do many orders

Order

@Entity
@Table(name = "Orders")
@XmlRootElement
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Order extends BasicModelWithIDInt {

    @Basic(optional = false)
    @Column(name = "Quantity")
    private Integer quantity;

    @Basic(optional = false)
    @Size(min = 1, max = 150)

    @Column(name = "Notes")
    private String notes;


    @JoinColumn(name = "ProductID", referencedColumnName = "ID")
    @ManyToOne(optional = false, fetch = FetchType.LAZY)
    @JsonIgnoreProperties
    private Product productID;

    @JoinColumn(name = "MerchantID", referencedColumnName = "ID")
    @ManyToOne(optional = false, fetch = FetchType.LAZY)
    private Merchent merchent;

    @JoinColumn(name = "OrderSatusID", referencedColumnName = "ID")
    @ManyToOne(optional = false, fetch = FetchType.EAGER)
    private OrderStatus orderStatus;


      // Getters and Setters
}

Order Holder

public class OrderHolder {

    @NotNull
    private Order order;

    public Order getOrder() {
        return order;
    }

    public void setOrder(Order order) {
        this.order = order;
    }
}

OrderRepo

public interface OrderRepo extends JpaRepository<Order, Integer> {
}

Order Controller

@RestController
@RequestMapping(value = "order", produces = MediaType.APPLICATION_JSON_VALUE)
public class OrderRestController extends BasicController<OrderHolder>{

    @Autowired
    private OrderRepo orderRepo;


 


    @PostMapping("create")
    public ResponseEntity<?> create(@RequestBody @Valid OrderHolder orderHolder, Principal principal) throws GeneralException {
        log.debug( "create order {} requested", orderHolder.toString());
        Order order = new Order();
        order = orderHolder.getOrder();
        System.out.println("###############"+order);
        try {
            order = orderRepo.save(order);
            log.info( "Order {} has been created", order );
        } catch (Exception e) {
            log.error( "Error creating Order:  ", e );
            e.printStackTrace();
            throw new GeneralException( Errors.ORDER_CREATION_FAILURE, e.toString() );
        }
        return ResponseEntity.ok( order );
    }

}

I need request body to look like the below instead of including the full Merchant and Product objects inside the request.

enter image description here

Upvotes: 0

Views: 1716

Answers (3)

Ravi Bhanushali
Ravi Bhanushali

Reputation: 155

I would like to share something regarding this. I have searched a lot on internet and tried lot of things, but the solution given here suited well for this scenario.

https://www.baeldung.com/jackson-deserialization

You need to create a Custom-deserializer for your model by extending StdDeserializer from com.fasterxml.jackson.databind.deser.std.StdDeserializer, where you just want to pass id's and not the whole object in the request.

I have given below example for User Model with Address object.

User(long userId, String name, Address addressId)
Address(long addressId, String wholeAddress)

Writing Deserializer for User class

public class UserDeserializer extends StdDeserializer<User> { 

public User() { 
    this(null); 
} 

public User Deserializer(Class<?> vc) { 
    super(vc); 
}

@Override
public User deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
    JsonNode node = p.getCodec().readTree(p);
    long id = 0;
    
    long addressId = (Long) ((IntNode) node.get("addressId")).numberValue().longValue();
    return new User(id, name, new Address(addressId, null)
    
}

Now you have to use

@JsonDeserialize(using = UserDeserializer.class)
public Class User {

...

}

POST request

Before custom deserialization

{
  "name" : "Ravi",
  "addressId" : { "id" : 1} 
}

After custom Deserialization

{
  "name" : "Ravi",
  "addressId" : 1
}

Also while GET /user/:id call you will get the whole obj like

{
  "name" : "Ravi",
  "addressId" : { "id" : 1, "wholeAddress" : "Some address"} 
}

Upvotes: 0

gaetan224
gaetan224

Reputation: 646

You can make use of JsonView to return only id of product and merchant

public class OrderView {}

...

        public class Product{

         @Id
         @JsonView(OrderView.class)
         private Integer id

         private String otherFieldWithoutJsonView
...
        }

and then in your controller

@PostMapping("create")
@JsonView(OrderView.class) // this will return the product object with one field (id)
    public ResponseEntity<?> create(@RequestBody @Valid OrderHolder orderHolder, Principal principal) throws GeneralException {
        ...
    }

hope this can help you

Upvotes: 2

Abhinaba Chakraborty
Abhinaba Chakraborty

Reputation: 3671

Just have a separate contract class.

public class OrderContract {
    private int merchantID;
    private String notes;
    ....
//getter, setters
}

public class OrderHolder {

   @NotNull
   private OrderContract orderContract;

   public OrderContract getOrderContract() {
       return orderContract;
   }

   public void setOrder(OrderContract orderContract) {
       this.orderContract = orderContract;
   }
}

And before making a call to the Repository , translate from OrderContract to Order.

Upvotes: 1

Related Questions