Lista uczestników wydarzenia - relacja @OneToMany

0

Mam 2 encje - Event i AppUser i chciałbym zrobić listę uczestników wydarzenia. Pierwsze co przyszło mi do głowy to relacja @OneToMany Event - AppUser, ale przy takiej relacji nie można przetestować metody repozytorium do pobierania wydarzeń i uczestników bez dodawania adnotacji @Transactional nad testem, bo przy odwoływaniu się do listy uczestników poleci LazyInitializationException, a też nie chcę żeby uczestnicy byli pobierani za każdym razem, więc pobieranie EAGER odpada. Może lepiej byłoby stworzyć jakąś encję pośredniczącą? Jest możliwość w jednokierunkowej relacji @OneToMany w encji Event wskazania pola event, które nie jest kluczem głównym w encji EventsUsers? Jak zrobiłem relację dwukierunkową @OneToMany i @ManyToOne a wydarzenie miało np. 6 uczestników to przy zapytaniu z JOIN'EM miałem łącznie 7 zapytań, które zwracały ten sam wynik. Jak taką relację najlepiej zaprojektować?

@Entity
@Table(name = "events_users")
@NoArgsConstructor
@Getter
public class EventsUsers {
    @EmbeddedId
    private EventsUsersId id;
    
    @Column(name = "event_id", nullable = false, insertable = false, updatable = false)
    private Event event;

    @Column(name = "user_id", nullable = false, insertable = false, updatable = false)
    private Long userId;

    public EventsUsers(Event event, Long userId) {
        this.id = new EventsUsersId(event.getId(), userId);
        this.event = event;
        this.userId = userId;
    }
}
0
Scarilt napisał(a):

miałem łącznie 7 zapytań

Więcej nie trzeba czytać. Źle wykorzystujesz ten framwork. Są sposoby żeby zrobić wszystko jednym zapytaniem.
https://www.baeldung.com/spring-data-jpa-named-entity-graphs
https://nullpointerexception.pl/hibernate-najczesciej-popelniane-bledy/
https://nullpointerexception.pl/hibernate-i-problem-n-plus-1-zapytan/

Generalnie JPA i konfigurowanie wszystkiego przez adnotacje nie jest takie różowe.

0

@Korges ale tutaj piszesz o relacji Event - AppUser, czy o relacji Event - EventsUsers?

0
Scarilt napisał(a):

Może lepiej byłoby stworzyć jakąś encję pośredniczącą?

Myślę że tak - bo to relacja Many-to-many, gdyż 1 user może uczestniczyć w wielu eventach, a na 1 event może przyjść więcej niż 1 user (no chyba że to jakiś meeting javowy, to wtedy max 1 osoba - prezenter).

1

ale przy takiej relacji nie można przetestować metody repozytorium do pobierania wydarzeń i uczestników bez dodawania adnotacji @Transactional nad testem

OK, nie znam się za dużo na JPA ale co złego jest w @Transactional nad testem?

Odpowiadając na pytanie to pewnie zgodnie z doktryną jest tak jak piszą wyżej zrobić many to many.

Z drugiej strony byłem kiedyś w projekcie gdzie jak jakaś kolekcja miała być leniwa i nie zawsze używana to zwyczajnie zapisywali IDki i potem wyciągali po ID, to oczywiście działa przy one-to-many, więc ty potrzebiwałbyś encje pośrednią tak jak mówisz. Ogólnie nie lubię JPA bo w prostych przypadkach jak masz prostego CRUDa to działa świetnie a jak chcesz coś bardziej skompikowanego to zaczynasz mieć problemy. Można go tak użyć troszkę, ale przy tych leniwych kolekcjach trzeba uważać

2
Scarilt napisał(a):

Mam 2 encje - Event i AppUser i chciałbym zrobić listę uczestników wydarzenia. Pierwsze co przyszło mi do głowy to relacja @OneToMany Event - AppUser, ale przy takiej relacji nie można przetestować metody repozytorium do pobierania wydarzeń i uczestników bez dodawania adnotacji @Transactional nad testem, bo przy odwoływaniu się do listy uczestników poleci LazyInitializationException, a też nie chcę żeby uczestnicy byli pobierani za każdym razem, więc pobieranie EAGER odpada. Może lepiej byłoby stworzyć jakąś encję pośredniczącą? Jest możliwość w jednokierunkowej relacji @OneToMany w encji Event wskazania pola event, które nie jest kluczem głównym w encji EventsUsers? Jak zrobiłem relację dwukierunkową @OneToMany i @ManyToOne a wydarzenie miało np. 6 uczestników to przy zapytaniu z JOIN'EM miałem łącznie 7 zapytań, które zwracały ten sam wynik. Jak taką relację najlepiej zaprojektować?

W zasadzie jest jedno źródło informacji jak optymalnie korzystać z JPA
https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/
blog Vlada

co do @Transactionala nad testem to nie ma w tym nic złego - to znaczy śmierdzi. Ale tak naprawdę kupą jest JPA + Spring, a ten @Transactional to mały smrodek dziągnący się za tymi technologiamil - ile byś nie naperfumował to i tak przebije.

0

@Pinek myślę, że nie ma potrzeby robić relacji @ManyToMany jeśli robimy taką dodatkową encję pośredniczącą.
@Korges @Pinek @KamilAdam @bbzzyyczczeek @jarekr000000 zrobiłem to tak i teraz rzeczywiście leci 1 zapytanie. Coś musiałem pomieszać ostatnio jak zakładałem ten wątek. To co widzicie poniżej jest okey czy można to jeszcze ulepszyć?

@Entity
@Table(name = "events")
@NoArgsConstructor
@Getter
@Setter
public class Event {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    @OneToMany(mappedBy = "event", cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<EventsAppUsers> participants = new HashSet<>();

    public Event(String name, Set<EventsAppUsers> participants) {
        this.name = name;
        this.participants = participants;
    }
}
@Entity
@Table(name = "events_app_users")
@NoArgsConstructor
public class EventsAppUsers {
    @EmbeddedId
    private EventsAppUsersId id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "event_id", referencedColumnName = "id", nullable = false, insertable = false, updatable = false)
    private Event event;

    @Column(name = "app_user_id", nullable = false, insertable = false, updatable = false)
    private Long appUserId;

    public EventsAppUsers(Event event, Long appUserId) {
        this.id = new EventsAppUsersId(event.getId(), appUserId);
        this.event = event;
        this.appUserId = appUserId;
    }
}
public interface EventRepository extends JpaRepository<Event, Long> {

    @Query("SELECT e FROM Event e LEFT JOIN FETCH e.participants")
    List<Event> findEventsAndItsParticipants();
}
1
Scarilt napisał(a):

@Pinek myślę, że nie ma potrzeby robić relacji @ManyToMany jeśli robimy taką dodatkową encję pośredniczącą.

Mówiąc many-to-many, chodziło mi o typ relacji, niekoniecznie o konkretną adnotację @ManyToMany :P I właśnie zaimplementowałeś relację many-to-many za pomocą encji pośredniczącej, co zresztą sugerowałem.

To co widzicie poniżej jest okey czy można to jeszcze ulepszyć?

Jedyne co bym dwukrotnie przetestował, to

cascade = CascadeType.ALL, orphanRemoval = true

czy jak usuniesz event, to czy przypadkiem Ci nie usunie również usera co poszedł na to wydarzenie.

0

@Pinek tylko, że w tej encji pośredniczącej nie mam relacji do AppUser. Takie coś nazywa się relacją many-to-many, czy dwukierunkową relacją one-to-many Event - EventsAppUsers?

czy jak usuniesz event, to czy przypadkiem Ci nie usunie również usera co poszedł na to wydarzenie.

Sprawdziłem i nie usuwa, bo w encji EventsAppUsers jest tylko ID użytkownika, sam Long, a nie obiekt AppUser.

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