Problem z filtrowaniem przy złączeniach za pomocą CriteriaApi Specification i JPQL

0

Posiadam Kilka encji

public class User extends implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @OneToMany(mappedBy="user")
    @EqualsAndHashCode.Exclude
    @ToString.Exclude
    private List<Book> books;
}

public class Book implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name="")
    private LocalDateTime effectiveDateTo;

    @EqualsAndHashCode.Exclude
    @ManyToOne
    @JoinColumn(name="")
    private User user;
    
    @OneToMany(mappedBy="Book")
    @EqualsAndHashCode.Exclude
    @ToString.Exclude
    private List<BookDocument> bookDocument = new ArrayList<>();
}

public class BookDocument implements Serializable {
    private static final long serialVersionUID = 1L;

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

    @Column(name="")
    private LocalDateTime dateTo;
    
    @ManyToOne
    @JoinColumn(name="")
    private Book Book;
}

Chcę pobrać konkretnego usera, ale z joinowaniem się do kolejnych tabel, tak żeby odfiltrować tylko pasujące rekordy - odfiltrowywuje najpierw nie pasujące z Book, a następnie z tych chcę wybrać właściwe z BookDocument.

Jedyne co udało mi się uzyskać, to zamiast JOIN User - Book, zrobiłem fetch i dodałem predykat z warunkami. Wtedy uzyskałem na "pierwszym" poziomie właściwy wynik, ale dla BookDocuments już to nie działa. Zresztą, nie pzowala mi hibernate na zrobienie dwóch fetchy. Próbowałem napisać sql i natywnie i z użyciem JPQL, ale efekt ten sam niezadawalający. Zmieniałem też typ złączenia na jawne wymuszenie EAGER, ale nie pomogło... zresztą, nie chce mieć eager, bo każdorazowo odnosząc się do usera nie potrzebuje tych kolejnych tabel pobierać...
Użycie join i warunków czy w on czy w wherze i tak zwraca wszystkie powiązania.... Nie wydaje mi się, żeby zwracanie zawsze wszystkich dowiązań było pożądane i poprawne, chyba, że jednak spring czy hibernate to wymusza, że tak ma być i koniec?
Próbowałem użyć JPAStreamer, ale póki co to chyba ten projekt jest w powijakach i nie da się zrobić złączenia A->B->C i jeszcze filtrować.
Da się w jakiś sposób wymusić stosowanie warunków where... czy jedyny sposób to querydsl i ręczne dłubanie sql?

0

Hm nie wiem, ale skoro już masz relacje 2-kierunkowa to spróbuj to wykorzystać i napisać query od strony BookDocument? Możesz sobie przejść do usera przez BookDocument.book.user

0

Trochę dziwna ta konstrukcja. Możesz napisać co chcesz osiągnąć robiąc takie encje?

0

@Charles_Ray: Z BookDocument czy z Book ? Teoretycznie z Book mógłbym zrobić bo fetch do 1 poziomu złączenia działał mi z warunkami, i zgrupować do usera.. tylko to jest wycinek encji. BookDocument może mieć kolejne złączenia i nie będę chciał wszystkich ich pobierać. Wydawało mi się, że jest to naturalny mechanizm, żeby nie wyciągać wszystkich powiązań, a tylko te które które spełniają where.

Tzn od BookDocument to tak średnio to widzę, bo chcę je pobrać dla konkretnego usera.
Edit. Od BookDocument wyjść nie mogę bo encja Book po za złączeniem z BookDocument ma dodatkowe na wzór BookDocument, które też muszę mieć.

ogólnie chodzi o :

select (troche z user, troche z book i troche z bookdocuments)  from user u 
left join book book on (........ - nie tylko po samych kluczach)
left join bookdocuments bd on ( bd.book = book.id....... - nie tylko po samych kluczach)
left join inna inna on ( inna.book = book.id....... - nie tylko po samych kluczach)
(i tak kilka ich)
where u.id = xcv

Czyli, chce pobrać jednego usera o konkretnym id, który ma 3453 powiązań z book i z tych powiązań tylko 3 spełniające mój warunek, i dla tych 3 powiazań, gdzie każde ma 3425 powiązań z bookdocuments, 95654 powiązań z bookdocuments2, bookdocuments3 itd tylko kilka spełniających warunki.
Nie wydaje mi się super optymalnym podejściem wziąć wszystko jak leci z i potem filtrować przy mapowaniu encji... po coś ktoś where wymyślił, a kiedyś nie było streamu w javie :P

P.S.
Zrobiłem na szybko test wychodząc od Book i chcąc zrobić fetch na tym samym poziomie, czyli fetch z book do bookdocuments, i innych złączeń one to Many w book, ale efekt taki sami, kiedy próbowałem zrobić fetch user do book i fetch z book do bookdocuments, czyli:
org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags

hibernate nie pozwala na wiele fetchy w jednym zapytaniu....

0

https://stackoverflow.com/questions/4334970/hibernate-throws-multiplebagfetchexception-cannot-simultaneously-fetch-multipl/51055523?stw=2#51055523

Natomiast prawie na pewno chcesz napisać SQL query i pobrać tylko te dane, które potrzebujesz. Ewentualnie pobrać same idki i potem dociągnąć encje, które chcesz modyfikować.

0

@Charles_Ray: widziałem wcześniej ten wpis na stacku, lecz nie za bardzo wiem jak mam go odnieść do mojego problemu, bo tam podejście jest troszkę inne...
U siebie mam spring data i wykorzystując repository chciałem zdefiniować kryteria wyszukiwania na zasadzie:

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

	default Optional<User> find(Long id) {
		return findOne(new Specification<User>() {
			@Override
			public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
				List<Predicate> predicatesList = new ArrayList<>();

				Join<User, Book> fetch = (Join<User, Book>) root.fetch(User_.books, JoinType.LEFT);
				Join<Book, BookDocument> fetch2 = Join<Book, BookDocument> fetch.fetch(Book_.bookDocument , JoinType.LEFT);
				
				predicatesList.add(cb.or(
//obsługa where - warunków jest kilka jak wspomniane na book, bookdcouments i id usera
						));
				
return cb.and(predicatesList.toArray(new Predicate[0]));
			}
		});
	}	
}

próbowałem w jpql wrzucić zapytanie, ale efekt taki sam... zły...

Natomiast prawie na pewno chcesz napisać SQL query i pobrać tylko te dane, które potrzebujesz

Tak chcę zrobić właśnie.

Nie za bardzo widzę, jak pobierać idki i dociągać... i to chyba gryzie się z relacyjnymi bazami...

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