简体   繁体   中英

get API in spring boot with one to many relation

I have two models that are having one to many relation (customers have many invoices) so i create one - many relation on it, this is my customer class :

@Entity
@Table(name = "customer")
public class Customer {
    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private int id;
    @Column(name = "serial_number")
    private long serialNumber;
    @Column(name = "first_name")
    private String firstName;
    @Column(name = "last_name")
    private String lastName;
    @Column(name = "email")
    private String email;
    @Column(name = "mobile_number")
    private String mobileNumber;
    @Column(name = "is_deleted")
    private boolean isDeleted;


    @OneToMany
    private Set <Invoice> invoices;
}

and this is invoices class :

@Entity
@Table(name = "invoice")
public class Invoice {

    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private int id;
    @Column(name = "serial_number")
    private long serialNumber;
    @Column(name = "status")
    private String status;
    @Column(name = "created_date")
    private Timestamp createdDate;
    @Column(name = "is_deleted")
    private boolean isDeleted;
    @ManyToOne
    @JoinColumn(name = "customer_id")
    private Customer customer;
}

and then i create GET API ( get customers ) but it's nor working and return this error :

nested exception is com.fasterxml.jackson.databind.JsonMappingException: could not extract ResultSet (through reference chain: java.util.ArrayList[0]->com.example.invoices.model.Customer["invoices"]), path=/customer/viewList}]

and this is my api :

public List<Customer> getAllCustomers() {
    List<Customer> customers =  cutomerRepository.findAll();
    return customers;
}

and controller :

   @GetMapping("/viewList")
    public ResponseEntity<List<Customer>> getAllCustomers() {
            List<Customer> customers = new ArrayList<>();
            customers = customerService.getAllCustomers();
            if (customers.isEmpty()) {
                return new ResponseEntity<>(HttpStatus.NO_CONTENT);
            }
            return new ResponseEntity<>(customers, HttpStatus.OK);
    }

You have a Bidirectional relation and therefore an endless loop if json tries to deserialize the Object.

You can use @JsonIgnore to break the loop or use DTOs to return at the endpoint

@Entity
@Table(name = "invoice")
public class Invoice {

    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private int id;
    @Column(name = "serial_number")
    private long serialNumber;
    @Column(name = "status")
    private String status;
    @Column(name = "created_date")
    private Timestamp createdDate;
    @Column(name = "is_deleted")
    private boolean isDeleted;
    @ManyToOne
    @JsonIgnore
    @JoinColumn(name = "customer_id")
    private Customer customer;
}

DTO would look something like this (I like to use records for this but since I don't know if you use Java 17 I still use class):

Customer:

@Data
public class CustomerDTO {
    private final int id;
    private final long serialNumber;
    private final String firstName;
    private final String lastName;
    private final String email;
    private final String mobileNumber;
    private final boolean isDeleted;

    private final Set <Invoice> invoices;

    public static CustomerDTO fromModel(Customer customer) {
        return new CustomerDTO(
            customer.getId(),
            customer.getSerialNumber(),
            customer.getFirstName(),
            customer.getLastName(),
            customer.getEmail(),
            customer.getMobileNumber(),
            customer.isDeleted(),
            customer.getInvoices()
                    .stream()
                    .map(InvoiceDTO::fromModel)
                    .collect(Collectors.toSet())
        );
    }
}

Invoice (here you don't show the customer again):

@Data
public class InvoiceDTO {
    private final int id;
    private final String status;
    private final Timestamp createdDate;
    private final boolean isDeleted;

    public static InvoiceDTO fromModel(Invoice invoice) {
        return new InvoiceDTO(
            invoice.getId(),
            invoice.getStatus(),
            invoice.getCreatedDate(),
            invoice.isDeleted()
        );
    }
}

Controller:

@GetMapping("/viewList")
public ResponseEntity<List<CustomerDTO>> getAllCustomers() {
   List<CustomerDTO> customers = new ArrayList<>();
   customers = customerService.getAllCustomers()
                              .stream()
                              .map(CustomerDTO::fromModel)
                              .toList() //Depending on Java Version .collect(Collectors.toList());
   if (customers.isEmpty()) {
      return new ResponseEntity<>(HttpStatus.NO_CONTENT);
   }
   return new ResponseEntity<>(customers., HttpStatus.OK);
}

Do not open the entity class directly to the outside world

As DTO use for example:

public class InvoiceDTO {

    private int id;
    private long serialNumber;
    private String status;
    private Timestamp createdDate;
    private boolean isDeleted;
    private CustomerDTO customer;
}

See it applied in my GitHub repo FurnitureStoreApplication, example DTO classes in package dto :

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM