A G
A G

Reputation: 102

Cannot get ressource from java spring api, probably jpa malfunction

My application is an Angular web app getting ressource from a java api with hibernate and jpa. I am going to paste relevant code in order of execution, and then the error (even if the error is not explicit).

Angular service calling back-end :

const API_URL = 'http://localhost:8080/communication/';

public getBugReports(): Observable<any> {
    return this.http.get(API_URL + 'bugreport');
}

Java controller receiving the request :

@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping("/communication/bugreport")
public class BugReportController {

    @Autowired
    BugReportRepository bugReportRepository;

    @RequestMapping(value = "", method = RequestMethod.GET)
    public List<BugReport> getBugReports() {
        return bugReportRepository.findAll();
    }

BugReportRepository is classic, the class extends JpaRepository<BugReport, Integer>

Entity BugReport :

@Entity
@Table(name="bugreport")
public class BugReport {

    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private int idBugReport;

    @ManyToOne
    @JoinColumn(name="idUser", nullable=false)
    private User user;

    private Date bugReportDate;

    @Size(max = 1000)
    private String text;

    @ManyToOne
    @JoinColumn( name="idSuggestionType" )
    @Cascade(org.hibernate.annotations.CascadeType.ALL)
    private SuggestionType suggestionType;


... Constructors, getters/setters etc...

Entity User :

@Entity
@Table( name = "users", 
        uniqueConstraints = { 
            @UniqueConstraint(columnNames = "username"),
            @UniqueConstraint(columnNames = "email") 
        })
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int idUser;

    @NotBlank
    @Size(max = 20)
    private String username;

    @NotBlank
    @Size(max = 50)
    @Email
    private String email;

    @NotBlank
    @Size(max = 120)
    private String password;

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable( name = "user_roles", 
                joinColumns = @JoinColumn(name = "user_id"), 
                inverseJoinColumns = @JoinColumn(name = "role_id"))
    private Set<Role> roles = new HashSet<>();

    @Column(name = "avatar", length = 1000)
    private byte[] avatar;

    @OneToMany(targetEntity = Suggestion.class, mappedBy="user")
    @Cascade(org.hibernate.annotations.CascadeType.ALL)
    private List<Suggestion> suggestions = new ArrayList<>();

... Constructors, getters/setters etc...

Entity Suggestion :

@Entity  @Table(name="suggestions")
public class Suggestion {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private int idSuggestion;

    @ManyToOne
    @JoinColumn(name="idUser", nullable=false)
    @Cascade(CascadeType.ALL)
    private User user;

    private Date suggestionDate;

    @Size(max = 1000)
    private String text;

    @ManyToOne
    @JoinColumn( name="idSuggestionType" )
    @Cascade(CascadeType.ALL)
    private SuggestionType suggestionType;

... Constructors, getters/setters etc...

Entity SuggestionType :

@Entity
@Table(name = "suggestion_types")
public class SuggestionType {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int idSuggestionType;

    @Enumerated(EnumType.STRING)
    @Column(length = 30)
    @Cascade(CascadeType.ALL)
    private ESuggestionType name;

... Constructors, getters/setters etc...

Front error :

HttpErrorResponse
error: {error: SyntaxError: Unexpected end of JSON input at JSON.parse (<anonymous>) at XMLHttpRequest.onL…, text: "[{"idBugReport":1,"user":{"idUser":1,"username":"E…ggestions\"])","path":"/communication/bugreport"}"}
headers: HttpHeaders {normalizedNames: Map(0), lazyUpdate: null, lazyInit: ƒ}
message: "Http failure during parsing for http://localhost:8080/communication/bugreport"
name: "HttpErrorResponse"
ok: false
status: 200
statusText: "OK"
url: "http://localhost:8080/communication/bugreport"
__proto__: HttpResponseBase

Stack trace back-end :

java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
at org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:472) ~[tomcat-embed-core-9.0.24.jar:9.0.24]
at javax.servlet.http.HttpServletResponseWrapper.sendError(HttpServletResponseWrapper.java:129) ~[tomcat-embed-core-9.0.24.jar:9.0.24]
.
.
.
[Request processing failed; nested exception is org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: com.arnaudg.springjwt.models.User["suggestions"]

(the rest is not very relevant)

Notice the StackOverflowError at the end, i think my jpa mapping is wrong, especially on the ManyToMany, OneToMany, JoinTable annotations. I am a front developper, so i'm all but an expert in java jpa.

I you find any error, or improvement in my code just tell me i'll be more than happy to know it. And if you want to see another class or else juste ask me. Thank you all

Upvotes: 1

Views: 53

Answers (1)

Mario Varchmin
Mario Varchmin

Reputation: 3782

The JSON conversion process that happens, when your controller sends the response back to the browser, does not understand the parent child relationship between the User and Suggestion objects. The User object contains a Suggestion (list) object, and the Suggestion entity contains a User object. So, when generating the JSON, the conversion runs into an infinite loop.

Annotate the suggestions property in the User entity with the @JsonManagedReference annotation, and the user property in the Suggestions entity with @JsonBackReference. This tells the JSON converter about the kind of relationship, and where to stop. See also here: Jackson - Bidirectional Relationships

Upvotes: 2

Related Questions