JPA połączenie 3 klas. Spring Data JPA

0

Cześć piszę gdyż już jestem ostro skołowany i nie wiem czy dobrze kombinuje.
Mam obiekt document, documentHistory i Usera.
Dokument wiadomo ma jakies pola, w documentHistory chce trzymać zmiany tego dokumentu z informacją Który user. Dokument może mieć wiele zmian przez tego samego usera lub kilku, każda zmiana ma date.
Troche kodu.
Klasa Document

@Entity
public class Document implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    @Column(unique = true,
            nullable = false)
    private UUID documentId;

    private LocalDateTime generateDate;
 
    @OneToMany(mappedBy="documentHistory")
    private Set<DocumentHistory> documentHistory; 
@Entity
public class UserAccount
        implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    @Column(unique = true,
            nullable = false)
    @NaturalId
    private String username;

    private String password;
    
    @OneToMany(mappedBy="personWhoChanged")
    private Set<DocumentHistory> workHistory; 
@Entity
public class DocumentHistory implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;
    
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="username")
    private UserAccount personWhoChanged;
    
    @ManyToOne
    @JoinColumn(name="documentId")
    private Document documentHistory;
    
    private LocalDateTime changedDate;
    

Problem mam gdy pobieram sobie testowo jakis dokument i wywalam JSON'a z zawartością to dostaje w polu documentHistory wiel wierszy z informacja na temat usera i apka sie wywala.
Nie mam zbytniego doświadczenia z JPA i nie widzę gdzie mogłem się walnąć. Mapowania chyba dobre są.
Był bym wdzięczny za jakieś wskazówki. Dodam że działam na PostgreSQL a łącze się wykorzystując Spring Data JPA.
Lekko obawiam się wiązania Usera z Historią nie wiem czy tutaj nie można by było coś uprościć.

0

To chyba dziala tak:

  1. Konwertowany jest object klasy Document do JSON-a, wiec konwerter ktory to robi skanuje sobie pola Twojej klasy. W pewnym momencie natrafia na pole documentHistory i zaczyna czytac kolekcje - tutaj jest prawdopodobnie wszystko ok.
  1. Po odczytaniu jakiegos elementu powyzszej kolekcji, konwerter stara sie przekonwertowac object DocumentHistory do formatu JSON. W koncu natrafia na pole personWhoChanged. Stara sie wiec przerobic obiekt klasy UserAccount na JSONa.

  2. Obiekt klasy UserAccont ma pole workHistory wiec trzeba kazdy element workHistory przerobic na JSONa. Problem polega na tym, ze jak zaczniemy te elementy konwertowac to w koncu trzeba bedzie wrocic znowu do konwertowania obiektu UserAccount i znowu do konertowania kolekcji workHistory.

Jezeli Twoim konwerterem jest Jackson to sprobuj dodac do pola workHistory adnotacje @JsonIgnore.

0

Dzięki za odpowiedź. Dodałem adnotację na polu, które wskazałeś jednak nie pomogło. Dodanie na documentHistory w klasie Document poskutkowało że zadziałało jednak troche za mało informacji mam teraz bez historii.

0

Pewnie nie zadzialalo dlatego, ze podobna sytuacje masz miedzy klasami Document i DocumentHistory. Klasa Document odwoluje sie do klasy DocumentHistory a klasa DocumentHistory do klasy Document.

Ustaw ignorowanie rowniez na polu documentHistory w klasie DocumentHistory - przy okazji warto byloby zmienic nazwe tego pola bo pole nie odwoluje sie do historii tylko do dokumentu.

Proponowalbym tez zapoznac sie z adnotacjami @JsonManagedReference oraz @JsonBackReference - nie stosowalem ale podobno sprawdzaja sie lepiej.

0

Cały problem leży w tym, że stosujesz podwójne wiązanie(nie wiem czy to prawidłowe tłumaczenie, chodzi o bi-directional refferences), gdzie zarówno rodzic ma referencje do swoich dzieci, jak i ta dzieci mają referencję do rodzica. Jest to o tyle wygodne, że możesz dostać się zarówno od rodzica do jego dzieci jak i od dziecka do rodzica(czyli dwukierunkowo). Jackson w takim przypadku głupieje(i to całkiem słusznie) przepełniając stos. Rozwiązania masz dwa:

1)Wprowadzić referencje jednokierunkowe, tj. np. od rodzica do dziecka, ale nie w drugą stronę.
2)Rozwiązać problem na poziomie biblioteki Jackson, tak jak poniżej chyba powinno działać

@Entity
public class Document implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    @Column(unique = true,
            nullable = false)
    private UUID documentId;

    private LocalDateTime generateDate;
 
    @JsonManagedReference
    @OneToMany(mappedBy="documentHistory")
    private Set<DocumentHistory> documentHistory; 
@Entity
public class UserAccount
        implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    @Column(unique = true,
            nullable = false)
    @NaturalId
    private String username;

    private String password;
    
    @JsonBackReference
    @OneToMany(mappedBy="personWhoChanged")
    private Set<DocumentHistory> workHistory; 
@Entity
public class DocumentHistory implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;
    
    @JsonManagedReference
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="username")
    private UserAccount personWhoChanged;
    
    @JsonBackReference
    @ManyToOne
    @JoinColumn(name="documentId")
    private Document documentHistory;
    
    private LocalDateTime changedDate;
    

Pozdrawiam

1 użytkowników online, w tym zalogowanych: 0, gości: 1