How to handle @JsonManagedReference and @JsonBackReference for three connected tables

Viewed 175

I am stuck on given scenario:
There are three entity
a) Bill (many to one relationship with vendor) [bi-directional] b) Vendor (one to many relationship with both vendor and vendorbank)
c) VendorBank (many to one relationship with vendor)[uni-directional]

Bills : Showing limited fields

@Entity
@Getter
@Setter
@ToString
@NoArgsConstructor
@Table(name="bill_details") 
public class Bills {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="bill_id")
    private int bill_id;
    
    @Column(name="bill_no")
    private String billno;
    
    @ManyToOne(cascade = {CascadeType.MERGE})
    @JoinColumn(name="b_vendor_id")
    private Vendors vendors;
    
        /* Args contructor code here */
             
    @JsonManagedReference
     public Vendors getVendors() { return vendors; }

VendorBank:Showing limited fields

public class VendorBank {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="v_bank_id")
    private int vendorBankId;
    
    @Column(name="v_acc_no")
    private String accountNumber;
    
    @Column(name="v_vendor_id")
    private int vendor_id;
    
    /*Both constructor code here*/
    }

Vendor Class:Showing imp fields only

/*Lombok code here*/    
     public class Vendors {
    
        @Id 
        @GeneratedValue(strategy=GenerationType.IDENTITY)
        @Column(name="vendor_id")
        private int vendor_id;
        
        @Column(name="vendor_name")
        private String vendor_name;
    
        @OneToMany(fetch = FetchType.LAZY,cascade = CascadeType.ALL)
        @JoinColumn(name="v_vendor_id")
        private List<VendorBank> vendorBank;
            
        @OneToMany(mappedBy = "vendors",cascade = {CascadeType.DETACH,CascadeType.MERGE,
                            CascadeType.PERSIST,CascadeType.REFRESH})
        private List<Bills> bills;
    
         @JsonBackReference 
        public List<Bills> getBills() {
            return bills;
        }
        //@JsonManagedReference()
        // @JsonIgnore
            public List<VendorBank> getVendorBank() {
                return vendorBank;
        }

Output:

{
            "bill_id": 102,
            "billno": "B-858",
            "vendors": {
                "vendor_id": 3,
                "vendor_name": "ABC Company",
                 "vendorBank": [
                    {
                        "vendorBankId": 14,
                        "accountNumber": "502998745002",
                        "vendor_id": 3
                    }
                ]
         }
        }

1.When I call vendor endpoint I get data from vendor + vendor bank as desired.
2.But When I call the Bill endpoints then I get data from Bill + vendor + vendorbank as above. I don't want vendor bank to come.[If I use JsonIgnore on vendorbank then i get the correct output but then above point 1 goes wrong]

1 Answers

OK, so what you want to achieve is to include some data from the entity in one context and not include it in another context.

I don't think you can do this by using purely annotations you put on entities, since they don't have the calling context.

So, what can we do here ?

We can use Jackson Mixins. For example:

class YourClass {
  public int ignoreThis() { return 0; }    
}

With this Mixin

abstract class MixIn {
  @JsonIgnore abstract int ignoreThis(); // we don't need it!  
}

With this:

objectMapper.getSerializationConfig().addMixInAnnotations(YourClass.class, MixIn.class)

And you can then use this to serialize the object into json in your controller (where you get it to the service) and then add it as the response body and send to the user.

It would be ideal if you could somehow configure the ObjectMapper, which is used by Spring behind the scenes to do this, however, this is not possible ( at least easily ), because you need to tie this objectMapper to your specific controller only and not all controllers.

Related