Home > Enterprise >  Spring Boot GET method returns duplicated values with StackOverFlow error
Spring Boot GET method returns duplicated values with StackOverFlow error

Time:05-23

I'm building a RESTful API GET method with Spring Boot to get return of a Bill entity as JSON from database. The return is not expected as it has many duplicated values and a StackOverFlowError.

[{"id":1,"date":"2022-05-20","time":"16:48:06","total":330000.0,"billDetails":[{"billMenuItemID":{"billId":1,"menuItemId":1},"bill":{"id":1,"date":"2022-05-20","time":"16:48:06","total":330000.0,"billDetails":[{"billMenuItemID":{"billId":1,"menuItemId":1},"bill":{"id":1,"date":"2022-05-20","time":"16:48:06","total":330000.0,"billDetails":[{"billMenuItemID":{"billId":1,"menuItemId":1},"bill":{"id":1,"date":"2022-05-20","time":"16:48:06","total":330000.0,"billDetails":[{"billMenuItemID":{"billId":1,"menuItemId":1},"bill":{"id":1,"date":"2022-05-20","time":"16:48:06","total":330000.0,"billDetails":[{"billMenuItemID":{"billId":1,"menuItemId":1},"bill":{"id":1,"date":"2022-05-20","time":"16:48:06","total":330000.0,"billDetails":[{"billMenuItemID":{"billId":1,"menuItemId":1},"bill":{"id":1,"date":"2022-05-20","time":"16:48:06","total":330000.0,"billDetails":[{"billMenuItemID":{"billId":1,"menuItemId":1},"bill":
//continues for eternity

Hibernate log:

Hibernate: 
    select
        bill0_.bill_id as bill_id1_0_,
        bill0_.date as date2_0_,
        bill0_.time as time3_0_,
        bill0_.total as total4_0_ 
    from
        bill bill0_
Hibernate: 
    select
        billdetail0_.bill_id as bill_id1_1_0_,
        billdetail0_.menu_item_id as menu_ite2_1_0_,
        billdetail0_.bill_id as bill_id1_1_1_,
        billdetail0_.menu_item_id as menu_ite2_1_1_,
        billdetail0_.quantity as quantity3_1_1_,
        billdetail0_.subtotal as subtotal4_1_1_,
        menuitem1_.menu_item_id as menu_ite1_2_2_,
        menuitem1_.description as descript2_2_2_,
        menuitem1_.img_url as img_url3_2_2_,
        menuitem1_.name as name4_2_2_,
        menuitem1_.price as price5_2_2_,
        menuitem1_.status as status6_2_2_,
        menuitem1_.type as type7_2_2_ 
    from
        bill_detail billdetail0_ 
    inner join
        menu_item menuitem1_ 
            on billdetail0_.menu_item_id=menuitem1_.menu_item_id 
    where
        billdetail0_.bill_id=?

How can I get a return of a Bill like this:

{
  "billId": 1,
  "date": 2022-05-20,
  "time": 16:48:06,
  "total": 330000,
  "billDetails": [
    {
      "menuItem": {
        "id": 1,
        "name": Rice,
        // other attributes of MenuItem
      },
      "quantity": 2
      "subtotal": 90000
    },
    {
      "menuItem": {
        "id": 2
        "name": Wine
        // other attributes of MenuItem
      },
      "quantity": 4
      "subtotal": 240000
    }
    ]
}

This is my classes and related functions

Class Bill

@Entity(name = "bill")
@Table(name = "bill")
public class Bill {

    @Id
    @GeneratedValue(
            strategy = GenerationType.IDENTITY
    )
    @Column(name = "bill_id")
    private Long id;

    private LocalDate date;
    private LocalTime time;
    private Double total;

    @OneToMany(mappedBy = "bill", cascade = CascadeType.ALL)
    private List<BillDetail> billDetails = new ArrayList<>();

Class MenuItem

@Entity
@Table(name = "menuItem",
        uniqueConstraints = {
                @UniqueConstraint(name = "menu_item_name_unique", columnNames = "name")
        }
)
public class MenuItem {

    @Id
    @GeneratedValue(
            strategy = GenerationType.IDENTITY
    )
    @Column(name = "menu_item_id")
    private Long id;
    private ItemType type;
    private String name;
    private String description;
    private String imgUrl;
    private Double price;
    private MenuItemStatus status = MenuItemStatus.ENABLED;

    @OneToMany(mappedBy = "menuItem", cascade = CascadeType.ALL)
    private List<BillDetail> billDetails = new ArrayList<>();

Class BillDetail

@Entity
@Table(name = "bill_detail")
public class BillDetail {
    @EmbeddedId
    private BillMenuItemID billMenuItemID = new BillMenuItemID();

    @ManyToOne
    @MapsId("billId")
    @JoinColumn(name = "bill_id")
    private Bill bill;

    @ManyToOne
    @MapsId("menuItemId")
    @JoinColumn(name = "menu_item_id")
    private MenuItem menuItem;

    @Column
    private Long quantity;

    @Column
    private Double subtotal;

GET method

@GetMapping
    public List<Bill> getMenuItems() {
        return billService.getBills();
    }

public List<Bill> getBills() {
        return billRepository.findAll();
    }

public interface BillRepository extends JpaRepository<Bill, Long> {
}

Database database

CodePudding user response:

In the class MenuItem you should add the annotation @JsonIgnore to prevent an infinite loop in the JSON format returned; a bill has a BillDetails , a BillDetails has a MenuItem , a MenuItem Has a BillDetails , every BillDetail has a List of MenuItem ...

@Entity
@Table(name = "menuItem",
        uniqueConstraints = {
                @UniqueConstraint(name = "menu_item_name_unique", columnNames = "name")
        }
)
public class MenuItem {

    @Id
    @GeneratedValue(
            strategy = GenerationType.IDENTITY
    )
    @Column(name = "menu_item_id")
    private Long id;
    private ItemType type;
    private String name;
    private String description;
    private String imgUrl;
    private Double price;
    private MenuItemStatus status = MenuItemStatus.ENABLED;
    
    // ADD JSON IGNORE ANNOTATION HERE :
    @JsonIgnore
    @OneToMany(mappedBy = "menuItem", cascade = CascadeType.ALL)
    private List<BillDetail> billDetails = new ArrayList<>(); 
  • Related