JPA fetch join limit

0

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?

0

Ale co ty rozumiesz przez limit na fetch join? Co chcesz limitować? Liczbę dołączonych wierszy czy co? Liczbę wyników?

0

Liczbę dołączonych wierszy. Chciałbym je posortować i ograniczyć. Tak żeby nie wczytywały się wszystkie tylko np. 10

0

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...

0

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 :)

0

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?

0

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!]]

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