Spring Unvollständinge Rückgabe bei Http-Request

Lisun4ik

Mitglied
Hallo,

ich habe eine OneToMany-Beziehung entwickelt in meiner Spring boot anwendung. Es sind gewisse Leistungen (services) mit Ausprägungen (expressions). Also kann eine Leistung mehrere Ausprägungen haben. Auf Java Seite sehe ich beim debuggen beim Abrufen von "findById" oder "findAll", dass der Rückgabewert die Liste der verknüpften Einträgen beinhaltet. Andererseits gibt mir Postman eine JSON ohne die Liste.

Service:
@Entity
public class Service {
...
@OneToMany(mappedBy = "service", targetEntity = Expression.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Expression> expressions = new ArrayList<Expression>();

public void addExpressions(List<Expression> expressions) {
this.expressions.addAll(expressions);
}
}

Expression:
@Entity
public class Expression {
...
@ManyToOne
@JoinColumn(name = "service_id")
private Service service;

public Service getService() {
return service;
}

public void setService(Service service) {
this.service = service;
}
}

Request:
@GetMapping("/service")
public ResponseEntity<Service> get(@RequestParam(value = "id") int id) {
Optional<Service> serviceInDB = serviceRepository.findById(id);
if (serviceInDB.isPresent()) {
return new ResponseEntity(serviceInDB.get(), HttpStatus.OK);
}
return new ResponseEntity("No service found with id " + id, HttpStatus.NOT_FOUND);
}

Responce:
{
"id": 1,
"serviceNumber": "0001",
"unit": "l",
"title": "Fliessestrich",
"description": "Lorem ipsum dolor sit amet.."
}

Meine Frage ist: Was kann ich tun, damit bei mir im Frontend die verknüpfte Daten mit auftauchen.
 

Lisun4ik

Mitglied
Sobald ich die hinzufüge kommt beim Ausführen von "find..." eine fehlermeldung:
* java.lang.instrument ASSERTION FAILED *: "!errorOutstanding" with message transform method call failed at s\src\java.instrument\share\native\libinstrument\JPLISAgent.c line: 884
2024-04-20T15:05:06.804+02:00 WARN 23024 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Failure while trying to resolve exception [org.springframework.http.converter.HttpMessageNotWritableException]

java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
at org.apache.catalina.connector.ResponseFacade.checkCommitted(ResponseFacade.java:503) ~[tomcat-embed-core-10.1.10.jar:10.1.10]
at org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:347) ~[tomcat-embed-core-10.1.10.jar:10.1.10]

...


2024-04-20T15:05:06.817+02:00 ERROR 23024 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError)] with root cause

java.lang.StackOverflowError: null
at java.base/java.lang.ClassLoader.defineClass1(Native Method) ~[na:na]
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1027) ~[na:na]

...

Meine Expressions-Entity hat auch eine 1:n Beziehung, könnte es daran liegen?
 

Oneixee5

Top Contributor
Sobald ich die hinzufüge kommt beim Ausführen von "find..." eine fehlermeldung:


Meine Expressions-Entity hat auch eine 1:n Beziehung, könnte es daran liegen?
Ja, es sieht so aus. Du kannst bei der Expression-Entity dem Feld: private Service service; die Annotation: @JsonIgnore verpassen. Damit sollte die "Infinite recursion" unterbrochen sein.

Es ist eine "bad practice" die Entities für die JSON-Serialization und die Rückgabe zu verwenden. Es ist auch aus Sicherheitsgründen zu empfehlen DTO's zu verwenden: https://www.baeldung.com/java-dto-pattern
Stell dir vor, du wirst die DB später einmal ändern. Bspw. fügst du einen ServiceUser zu Service hinzu. Jetzt serialisiert deine Entity automatisch ServiceUser mit in den Response. ServiceUser kann jetzt auch noch sicherheitsrelevante Informationen enthalten. Du würdest diese Informationen ungewollt automatisch offenlegen.
Zum Lernen und Probieren ist das OK, du solltest das aber im Hinterkopf behalten.
 
Zuletzt bearbeitet:

Lisun4ik

Mitglied
Das Hinzufügen von @JsonIgnore zum Service an der Excpression hat dazu geführt:
2024-04-20T16:49:53.859+02:00 WARN 14036 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Failure while trying to resolve exception [org.springframework.http.converter.HttpMessageNotWritableException]

java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
at org.apache.catalina.connector.ResponseFacade.checkCommitted(ResponseFacade.java:503) ~[tomcat-embed-core-10.1.10.jar:10.1.10]

Und deinen zweiten Punkt, habe glaube ich nicht richtig verstanden. Ich habe BOs für die Entity-Objekte, jeweils ein Repository und ein Controller mit "ResponseEntity<Service>" als Rückgabewert. Ist das unsicher?

Hier ist meine Service-Methode:
@GetMapping("/service")
public ResponseEntity<Service> get(@RequestParam(value = "id") int id) {
Optional<Service> serviceInDB = serviceRepository.findById(id);
if (serviceInDB.isPresent()) {
return new ResponseEntity(serviceInDB.get(), HttpStatus.OK);
}
return new ResponseEntity("No service found with id " + id, HttpStatus.NOT_FOUND);
}

Mich wundert, dass es ja eigentlich gut läuft und das Objekt die excpressions vorhanden sind beim Rückgabewert. Sie werden blos nicht im Frontend angezeigt.

Vielen Dank für deine Hilfe und die schnelle Rückmeldungen!
 

Oneixee5

Top Contributor
Die Service-GET-Methode würde ich so schreiben:
Java:
@GetMapping("/service/{id}")
Service getServiceById(@RequestParam("id") Integer id) {
    return this.serviceRepository.findById(id).orElseThrow(() ->
        new ResponseStatusException(HttpStatus.NOT_FOUND,
                String.format("No service found with id: %s!", id));
    );
}
Der Aufruf wäre dann wie folgt:
Code:
http://localhost:8080/service/99
Host, Port und Id muss natürlich angepasst werden, wenn diese bei dir abweichen.

[Also aus dem Kopf geschrieben, ...]

Edit: ich denke du fragst den Parameter id ab aber den gibt es gar nicht:
@GetMapping("/service")
@RequestParam(value = "id")
Vermutlich müsstest du nur @RequestParam schreiben, wenn du keinen benannten Parameter hast.
 
Zuletzt bearbeitet:

Ullenboom

Bekanntes Mitglied
Wenn ich tippen müsste: der Klassiker bei bidirektionalen Beziehungen:

Code:
2024-04-20T15:05:06.817+02:00 ERROR 23024 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: 
org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError)] with root cause

A verweist auf B und B verweist wieder auf A. Wenn du das Jackson gibst, läuft er unendlichen den Objektbaum ab.

Es gibt verschiedene Lösungen, genannt wurden schon @JsonIgnore und DTO-Objekte, die dann keine Rück-Referenz haben. Auch @JsonBackReference löst das Problem.
 

Oben