Witajcie,
Dzięki pomocy jednego z moderatorów forum dowiedziałem się jak FetchType.EAGER zastąpić LAZY wraz z FETCH JOIN w zapytaniu i nie ładować całej bazy przy rozbudowanych relacjach. Mój problem polega teraz na tym że potrzebuję zrobić ORDER i LIMIT na tabeli która jest fetch joinowana. Znalazłem taki artykuł, lecz tyczy się on Hibernate.
http://www.mkyong.com/hibernate/hibernate-fetching-strategies-examples/
Ja staram się korzystać tylko z JPA i stąd też moje pytanie.
Czy w JPA jest to w ogóle możliwe?
- Rejestracja:około 10 lat
- Ostatnio:prawie 5 lat
- Postów:174

- Rejestracja:około 21 lat
- Ostatnio:prawie 3 lata
- Lokalizacja:Space: the final frontier
- Postów:26433
Ale co ty rozumiesz przez limit na fetch join? Co chcesz limitować? Liczbę dołączonych wierszy czy co? Liczbę wyników?
- Rejestracja:około 10 lat
- Ostatnio:prawie 5 lat
- Postów:174
Liczbę dołączonych wierszy. Chciałbym je posortować i ograniczyć. Tak żeby nie wczytywały się wszystkie tylko np. 10

- Rejestracja:około 21 lat
- Ostatnio:prawie 3 lata
- Lokalizacja:Space: the final frontier
- Postów:26433
Nie da się bo w SQL nie istnieje w ogóle coś takiego. Mógłbyś ewentualnie kombinować z joinowaniem wyniku podzapytania ale to co robisz generalnie wygląda raczej na błąd projektowy...
- Rejestracja:około 10 lat
- Ostatnio:prawie 5 lat
- Postów:174
Podglądnąłem zapytanie generowane przez JPA. Jest ono poprawne, lecz brakuje w nim LIMIT.
Wygląda ono tak:
SELECT DISTINCT ...
FROM categories
INNER JOIN topics ON categories.category_id=topics.category_id
WHERE LOWER(categories.name)=LOWER(?)
Encja Category:
@Entity
@Table(name = "categories")
@NamedQueries({
@NamedQuery(name = "Category.findOneForCategoryPage", query = "SELECT DISTINCT c FROM Category c JOIN FETCH c.topics WHERE LOWER(c.name) = LOWER(:name)")
})
public class Category implements Serializable {
@Column(nullable = false, length = 64)
private String name;
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "category_id")
private List<Topic> topics;
/* Pozostałe pola, które usunałem żeby post nie był ogromny, a nie grają tu roli oraz gettery i settery */
}
Również okrojona encja Topic:
@Entity
@Table(name = "topics")
public class Topic implements Serializable {
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "category_id")
private Category parent;
@Column(nullable = false, length = 64)
private String name;
}
Jak już wspominałem potrzebuję ograniczyć ilość rekordów dołączanych do listy topics za pomocą "FETCH JOIN c.topics" w JPA Named Query. Dodając do zapytania SQL na końcu LIMIT np. 1 zapytanie zwróci poprawny wynik z ograniczoną ilością rekordów.
Poprawne rozwiązanie w SQL:
SELECT DISTINCT categories.category_id, topics.topic_id, categories.avatar_id, categories.description, categories.name, categories.section_id, topics.name, topics.category_id, topics.category_id, topics.topic_id
FROM categories
INNER JOIN topics ON categories.category_id=topics.category_id
WHERE LOWER(categories.name)=LOWER(?)
LIMIT ?
Próbowałem też zrobić LIMIT w Query zwracanym przez EntityManager'a
return (Category)this.em.createNamedQuery("Category.findOneForCategoryPage").setParameter("name", category).setFirstResult(0).setMaxResults(1).getSingleResult();
Niestety bezskutecznie ponieważ setFirstResult i setMaxResults nie było brane w ogólne pod uwagę w zapytaniu wygenerowanym przez JPA - zapytanie wyglądało tak jak pierwsze w tym poście.
Mam nadzieję że w końcu dostatecznie dokładnie się wypowiedziałem i uzyskam pomoc, ponieważ długo już borykam się z tym problemem :)

- Rejestracja:około 21 lat
- Ostatnio:prawie 3 lata
- Lokalizacja:Space: the final frontier
- Postów:26433
Zauważ że twój limit
nie jest w standardzie SQL. Co więcej nie jest nawet zbyt popularny ;) MSSQL ma top
i to podawane na początku zapytania a Oracle wymaga podzapytania z użyciem zmiennej rownum. Twój limit zadziała tylko w mysql i postgresql.
Niemniej set max results powinno poprawnie zadziałać. Jesteś pewien że do bazy poleciało niepoprawne zapytanie?
- Rejestracja:około 10 lat
- Ostatnio:prawie 5 lat
- Postów:174
Tego nie wiedziałem, mój błąd. Korzystam z MySQL.
Pokombinowałem trochę i okazało się że JOIN FETCH nie działa z setMaxResults
W zapytaniu poniżej setMaxResults działa (tzn. jest dodawane do zapytania), ale musiałem zrezygnować z JOIN FETCH na rzecz FetchType.EAGER, gdzie i tak fetchowane są wszystkie wyniki:
return (Category) this.em.createQuery("SELECT DISTINCT c FROM Category c WHERE LOWER(c.name) = LOWER(:name)")
.setParameter("name", category)
.setFirstResult(0)
.setMaxResults(1)
.getSingleResult();
Wygenerowane zapytanie:
SELECT DISTINCT ...
FROM categories
WHERE LOWER(name)=LOWER(?)
LIMIT ?
Natomiast w zapytaniu poniżej jest dodany FETCH JOIN, ale nie działa setMaxResults:
return (Category) this.em.createQuery("SELECT DISTINCT c FROM Category c JOIN FETCH c.topics WHERE LOWER(c.name) = LOWER(:name)")
.setParameter("name", category)
.setFirstResult(0)
.setMaxResults(1)
.getSingleResult();
Wygenerowane zapytanie:
SELECT DISTINCT ...
FROM categories
INNER JOIN topics ON categories.category_id=topics.category_id
WHERE LOWER(categories.name)=LOWER(?)
@Edit
Dodam jeszcze że podczas wykonywania zapytania z JOIN FETCH w logach można zobaczyć taką informację
[2015-04-14T21:18:14.504+0200] [glassfish 4.1] [WARN] [] [org.hibernate.hql.internal.ast.QueryTranslatorImpl] [tid: _ThreadID=30 _ThreadName=http-listener-1(1)] [timeMillis: 1429039094504] [levelValue: 900] [[
HHH000104: firstResult/maxResults specified with collection fetch; applying in memory!]]