Home > Net >  Spring Boot REST Api - Response data is not showing expected results using 1:M relationship between
Spring Boot REST Api - Response data is not showing expected results using 1:M relationship between

Time:10-02

So I'm trying to build a website using Spring Boot and React.

So far I have a registration form that is connected to my spring backend at localhost:8080/api/test/customers (Have endpoints for CRUD operations here)

This woks fine, and I can post form data to it from my React form, and I can view it in postman to get the expected response from the API.


But now I have another form on the site (A contact form where a user enters their name, email and message).

I have this API at localhost:8080/api/form/contact for GET and POST requests.


I have mapped the Entities 'Customer' and 'Contact Form' using a 1:M relationship which contains the customer id as a FK in the Contact Form table.

The thing is, since I added this, The response from the API is not what I'm expecting.


The response goes on for like this for 200 lines in postman, just nesting the same stuff

The GET request to the Customer API (registration)

The GET request to the Contact Form API


My Entity Classes

Customer.java - Removed Validation Annotations to make it easier to read

@Entity
@Table(name="customer")
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="customer_id")
    private int customerId;

    @Column(name="first_name")
    private String firstName;

    @Column(name="last_name")
    private String lastName;

    @Column(name="email_address")
    private String emailAddress;

    @Column(name="date_of_birth")
    private Date dateOfBirth;

    @Column(name="mobile_number")
    private String mobileNumber;

    @Column(name="password")
    private String password;

    @OneToMany(mappedBy = "customer")
    private Set<ContactForm> contactForm;

    // default & arg constructor
    // getters and setters

}



ContactForm.java - Removed Validation Annotations to make it easier to read

@Entity
@Table(name="contact_form")
public class ContactForm {

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

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

    @Column(name="email_address")
    private String emailAddress;

    @Column(name="message")
    private String message;

    @ManyToOne
    @JoinColumn(name = "customer_id")
    private Customer customer;

    // default and arg constructor
    // getters & setters

}

MySQL DDL for these 2 tables

customer


CREATE TABLE `customer` (
  `customer_id` int NOT NULL AUTO_INCREMENT,
  `mobile_number` varchar(12) DEFAULT NULL,
  `date_of_birth` date DEFAULT NULL,
  `email_address` varchar(255) DEFAULT NULL,
  `first_name` varchar(255) DEFAULT NULL,
  `last_name` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`customer_id`)
)

contact_form


CREATE TABLE `contact_form` (
  `contact_form_id` int NOT NULL AUTO_INCREMENT,
  `email_address` varchar(255) NOT NULL,
  `message` varchar(255) NOT NULL,
  `name` varchar(255) NOT NULL,
  `customer_id` int DEFAULT NULL,
  PRIMARY KEY (`contact_form_id`),
  FOREIGN KEY (`customer_id`) REFERENCES `customer` (`customer_id`)
) 

Controller class for customer

Controller Class with standard endpoints, same for ContactForm Controller

I cant post data to either endpoints anymore, Can someone please let me know why this is happening, I would like to have a record of the users and all their previous messages.

CodePudding user response:

I would highly suggest to look at the DTO pattern to expose your domain objects over REST, with just the necessary information included.

Exposing ORM entities directly is error prone, not abstracting API internals and can even create major security risks.

For example, create UserDto and MessageDto classes:

public class UserDto {
  private int customerId;
  private List<MessageDto> messages;
}

public class MessageDto {
  private String content;
  private String recipient;
  // other relevant fields
}

and return the UserDto (or a list thereof) from your rest controller method instead.

The actual problem you are describing is that your ORM mapping builds a circular graph between Customer and ContactForm. The @OneToMany/@ManyToOne relation seems to be not working correctly.
In general I would suggest to not map Entities in such a cyclic way, but make them navigable in only one direction (e.g. Customer->ContactForm or the other way round).
Cyclic mappings (and big graphs in general) can cause automatic, unexpected loading of very big parts of your database in memory for seemingly small queries.

CodePudding user response:

Regardless of using DTO or not, you referred Customer which refers ContactForm which refers Customer again, causing a recursive reference to the object.

A simple way to avoid this is to use @JsonManagedReference and @JsonBackReference, which is described here.

Another way to do it is to @JsonIgnore on the child object, depending on how your API interacts.

Both of these would tell Jackson to ignore the child object on Serialization.

  • Related