Reputation: 187
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);
}
Upvotes: 1
Views: 612
Reputation:
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
:
Upvotes: 0
Reputation: 1178
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);
}
Upvotes: 3