Rekursive Verschachtelung bei den Entitäten

ExceptionOfExpectation

Bekanntes Mitglied
Ich habe ein SpinBoot3 Projekt und dafür habe ich zuerst auf der Datenbank Seite die Datenbank mit den Tabellen erstellt und zum Schluss mit den Datensätzen gefüllt. Im ganzen wurde die Datenbank rechtkompliziert: alle Beziehungsarten, Bild-Formate, selten verwendete Typen usw. Die Constraint und Indexierung habe ich komplett ausgelassen, da dadurch die Fremdschlüssel nicht redundant sein dürfen.
Auf der Java-Seite habe Ich Entitäten, die durch bidirektionale Beziehung miteinander verknüpft sind, so ein Beispiel:

Java:
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Data
@Access(AccessType.FIELD)
public class Profile {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;
   
    @Column(length=300)
    private String login;
   
    @Column(name="firstname",
            length=200)
    private String firstName;
   
    @Column(name="lastname",
            length=250)
    private String lastName;
   
    private enum Gender{ Male, Female}
   
    private LocalDate birthdate;
   
    @ManyToMany(cascade = CascadeType.ALL)
    @JoinTable(
            name = "Detail_Crud_Profile",
            joinColumns = {    @JoinColumn(name="profile_fk")},
            inverseJoinColumns = { @JoinColumn(name="crud_fk")}
            )
    private List<Crud> cruds=new ArrayList<>();
       
    @OneToOne(mappedBy="profile", fetch=FetchType.EAGER, cascade=CascadeType.ALL)
    private Address address;
   
    @OneToOne(mappedBy="profile", fetch=FetchType.EAGER, cascade=CascadeType.ALL)
    private Avatar avatar;
   
    @OneToOne(mappedBy="profile", fetch=FetchType.EAGER, cascade=CascadeType.ALL)
    private Point point;
   
    @OneToMany(mappedBy="profile", fetch= FetchType.EAGER, cascade=CascadeType.ALL)
    private List<Post> post = new ArrayList<>();
   

}

-----------------------------------------------------
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
@Access(AccessType.FIELD)
public class Post {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;
    @Column(columnDefinition="MEDIUMTEXT")
    private String message;
    private int likes;
   
    @ManyToOne(fetch=FetchType.EAGER)
    @JoinColumn(name="profile_fk", referencedColumnName= "id")
    private Profile profile;

}

Die Entities werden von Repositories durch Service direkt zum Controler geleitet:

Java:
import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import de.astro.entity.Profile;
import de.astro.service.ProfileService;

@RestController
public class AuthenticationController {
   
    @Autowired
    private ProfileService profileService;
   
   
   
    @GetMapping("/profiles")
    public ResponseEntity<List<Profile>> getProfiles(){
        return ResponseEntity.ok(profileService.findAll()) ;
    }
   
    @GetMapping("/profiles/{id}")
    public Optional<Profile> getProfile(@PathVariable Long id) {
        return Optional.ofNullable(profileService.findById(id)).orElse(null);
    }
   
}
Bei dem Aufruf von "http://localhost:8080/profiles" habe ich immer wieder rekursive Verschachtelung von Daten erhalten. Das heißt Profile ruft die Tabelle Post auf und die Tabelle Post ruft wiederum die Tabelle Profile auf und das solange bis SpringBoot oder auch JsonFormat nicht mehr kann. Ein Beispiel von der Verschachtelung im Anhang.
Bitte, erklärt mir wie ich es verhindern kann. Durch die ChatGPT habe ich den Ansatz mit @JsonManagedReference und @JsonBackReference versucht die Verschachtelung auszuschließen. Es hat zwar funktioniert, aber ich hätte gerne eine bessere Lösung gewünscht.
PS.: Für diejenigen, die nicht aus Programmierwelt sind und einen Untertext suchen, oder durch die Begriffe getriggert fühlen: sucht hier keine verschlüsselte Botschaft!
 

Anhänge

  • api.jpg
    api.jpg
    606,5 KB · Aufrufe: 0
Zuletzt bearbeitet:

Oneixee5

Top Contributor
Die Möglichkeiten sind: @JsonManagedReference, @JsonBackReference, @JsonIgnoreProperties oder @JsonIgnore. Andernfalls bleibt nur die Möglichkeit, die Rekursion in den Entities gar nicht erst anzulegen.
Da du FetchType.EAGER verwendest muss eigentlich Hibernate das selbe Problem haben. Ich denke du solltest mal die SQL-Abfragen loggen und schauen was alles geladen wird.
 

ExceptionOfExpectation

Bekanntes Mitglied
Die Möglichkeiten sind: @JsonManagedReference, @JsonBackReference, @JsonIgnoreProperties oder @JsonIgnore. Andernfalls bleibt nur die Möglichkeit, die Rekursion in den Entities gar nicht erst anzulegen.
Da du FetchType.EAGER verwendest muss eigentlich Hibernate das selbe Problem haben. Ich denke du solltest mal die SQL-Abfragen loggen und schauen was alles geladen wird.
Habe auch mit FetchType.LAZY versucht, leider dasselbe. Hibernate loggt schon selbst, es werden alle Daten geladen. Nur aufgrund der Verschachtelung werden sie nicht mit angezeigt. JSON Format kann sie nicht mehr aufnehmen.

Also gibt es keine andere Möglichkeiten?
 

Oneixee5

Top Contributor
Habe auch mit FetchType.LAZY versucht, leider dasselbe. Hibernate loggt schon selbst, es werden alle Daten geladen. Nur aufgrund der Verschachtelung werden sie nicht mit angezeigt. JSON Format kann sie nicht mehr aufnehmen.

Also gibt es keine andere Möglichkeiten?
Du hast mich nicht richtig verstanden. Vermutlich werden von Hibernate zu viele Abfragen ausgeführt, da du rekursiv mit FetchType.EAGER alles lädst was irgendwie erreichbar ist. Das fällt jetzt nicht auf - mit wenigen Testdaten, könnte aber zu einem Problem werden, wenn du in Produktion gehst. Hibernate ist zwar schlau und mächtig, man kann sich aber den ganzen Tag versauen, wenn man nicht ganz genau weiß was man tut.

Eine Entity im Response zurückgeben, dass dürftest du bei uns auch gar nicht erst machen. Damit würdest du durch den Audit fallen. @Json-Annotations gehören an/in DTO's - nicht an Entities. Da Spring Boot alles so einfach macht, wird es zu PHP verkommen - dort darf man auch alles an alle Orten im Programm. Die Folge ist, dass Spring Boot als unsicher verschrien wird, dabei sind die Nutzer von Spring Boot die Schwachstelle.
 

ExceptionOfExpectation

Bekanntes Mitglied
Du hast mich nicht richtig verstanden. Vermutlich werden von Hibernate zu viele Abfragen ausgeführt, da du rekursiv mit FetchType.EAGER alles lädst was irgendwie erreichbar ist. Das fällt jetzt nicht auf - mit wenigen Testdaten, könnte aber zu einem Problem werden, wenn du in Produktion gehst. Hibernate ist zwar schlau und mächtig, man kann sich aber den ganzen Tag versauen, wenn man nicht ganz genau weiß was man tut.

Eine Entity im Response zurückgeben, dass dürftest du bei uns auch gar nicht erst machen. Damit würdest du durch den Audit fallen. @Json-Annotations gehören an/in DTO's - nicht an Entities. Da Spring Boot alles so einfach macht, wird es zu PHP verkommen - dort darf man auch alles an alle Orten im Programm. Die Folge ist, dass Spring Boot als unsicher verschrien wird, dabei sind die Nutzer von Spring Boot die Schwachstelle.
Ich verstehe was du meinst, aber ich habe höchstens 14 Tabellen, wenn man die Detailtabellen nicht berücksichtigt. Sie lassen sich sehr kompakt abbilden, Spring Boot hat kein Problem damit. Das Problem liegt an den Schlüsselbeziehungen. Profile ruft zuerst die Post Entity auf, dann ruft Post Entity die Profile Entity und die Profile Entity ruft wieder Post Entity auf und so wiederholt es sich bis Json-Format nicht mehr kann. Der Rest der Tabellen kommen gar nicht dran. Sonst gibt es keine Probleme auch wenn es Daten aus allen Tabellen sind.
 
Ähnliche Java Themen

Ähnliche Java Themen

Neue Themen


Oben