Mapowanie bazy w JPA w praktyce

Mapowanie bazy w JPA w praktyce
ZA
  • Rejestracja:około 10 lat
  • Ostatnio:prawie 5 lat
  • Postów:174
0

Witajcie,
Mam do was stricte teoretyczne pytanie. Korzystam z JPA. Mam napisane proste forum (struktura relacji tabeli w załączniku). Mój problem polega na tym, że gdy na stronie głównej chcę wczytać wszystkie sekcje kategori, orm automatycznie wczytuje wszystkie kategorie (bo mam tak zdefiniowaną relacje), następnie wszystkie tematy, potem wszystkie posty z tematów, a następnie wszystkich userów, którzy dodali jakikolwiek post. Jak zapewne nie trudno się domyśleć, jest to znaczna część bazy. Większość tych danych nie jest mi potrzebna na stronie głównej. Dlatego wychodzę do was z pytaniem. Jak ograniczyć ilość wczytywanych danych przez orma, nie rezygnując z relacji? Czytałem o transakcjach i o LAZY initialization, ale nie mam pomysłu jak użyć tego w praktyce. Powinienem wykonać metodę z JpaRepository z poziomu DAO i jeszcze przed zakończeniem wykonywania metody, która ma adnotację @Transactional powołać się na pole, które będzie potrzebne do wyników zwracanych przez tą metodę? Prosiłbym o jakieś porady lub gotowe projekty na githubie, na których mógłbym się wzorować :)

Pozdrawiam

Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
1

Oznacz po prostu w klasach encyjnych odpowiednie pola przez FetchType na Lazy. Ale pamiętaj że tam gdzie jednak chcesz wyciągać pozostałe dane musisz dodawać fetch joiny do zapytań, bo inaczej nie będzie ci pobierać tych powiązanych pól.


"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
shagrin
  • Rejestracja:prawie 17 lat
  • Ostatnio:ponad 6 lat
  • Lokalizacja:Norwegia, Stavanger
0

Na pewno będzie łatwiej jeśli załączysz również reprezentację tej struktury w Javie.

Jeśli chodzi o użycie LAZY, to przed joinem używasz odpowiedniej adnotacji, np:

Kopiuj
@Entity
@Table(name = "post")
public class Post implements Serializable{
	@Id
 	private String id;
 	private String title;
 	private String text;

	@OneToMany(fetch=FetchType.LAZY, mappedBy="comment")
 	private List<Comment> comments;

 // setters and getters
}

edytowany 1x, ostatnio: shagrin
niezdecydowany
niezdecydowany
@OneToMany na default na lazy
shagrin
tak, to fakt, jednak zaznaczyłam gdzie i jak się tego używa, żeby było widać - tego dotyczy ten temat
ZA
  • Rejestracja:około 10 lat
  • Ostatnio:prawie 5 lat
  • Postów:174
0

Mam już zmapowaną bazę do obiektów. @Shalom fetch joiny mam robić po prostu w taki sposób, że przed zamknięciem sesji muszę wykonać metodę? Taką którą mógłbym pobrać ten obiekt do którego odnosi się relacja?

edytowany 1x, ostatnio: Zaprogramowany
Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
0

Nie, to jest idiotyczny pomysł i spowoduje n+1 selectów. Masz w zapytaniu ktorym wyciągasz dane z bazy dodać fetch join. Ale póki nie pokażesz jakiegokolwiek kodu to wróżymy z fusów...


"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
shagrin
  • Rejestracja:prawie 17 lat
  • Ostatnio:ponad 6 lat
  • Lokalizacja:Norwegia, Stavanger
1

Jeszcze w ramach ciekawostki:
Możesz pobrać tylko te dane, które Cię interesują, poprzez odpowiednio skonstruowane zapytanie, które wykorzystuje konstruktor. W takim przypadku, Twoje encja musi mieć zdefiniowany konstruktor, który przyjmuje te parametry, które chcesz pobrać, np:

Kopiuj
	public class Post implements Serializable{
	@Id
 	private String id;
 	private String title;
 	private String text;

	@OneToMany(fetch=FetchType.EAGER, mappedBy="comment")
 	private List<Comment> comments;

	public Post(){}

	public Post(String title, String text){
		//...
	}

 	// setters and getters
}

Następnie piszesz zapytanie w postaci:

Kopiuj
	SELECT NEW pelna.nazwa.paczki.Post(p.title,p.text)
	FROM Post p

Zwróć uwagę na 'NEW' oraz fakt, że musi być podana pełna nazwa paczki, w której znajduje się encja.

To zapytanie zwróci tylko title i text, nie zależnie od sposoby pobrania danych z łączonych tabel (EAGER, czy LAZY). Co jest w tym fajne, to że nie musisz się później martwić o joiny jak potrzebujesz wszystkich danych i dostajesz prawidłową strukturę obiektu, nie ma więc problemu ze zwróceniem np. JSONa.

W takiej encji możesz zdefiniować kilka zapytań (@NamedQuery), które później tylko wywołujesz w kodzie.


ZA
  • Rejestracja:około 10 lat
  • Ostatnio:prawie 5 lat
  • Postów:174
0

No to np. klasy:

Kopiuj
@Entity
@Table(name = "topics")
public class Topic {

    @Id
    @GeneratedValue
    @Column(name = "topic_id")
    private Integer id;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "category_id")
    private Category parent;

    @Column(nullable = false, length = 64)
    private String name;

    @OneToMany(fetch = FetchType.EAGER)
    @JoinColumn(name = "topic_id")
    private List<Post> posts;

    /* Getters and Setters */

}
Kopiuj
@Entity
@Table(name = "posts")
public class Post {

    @Id
    @GeneratedValue
    @Column(name = "post_id")
    private Integer id;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "topic_id")
    private Topic parent;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "user_id")
    private User creator;

    @Column(nullable = true)
    private Date date;

    @Column(length = 2^16)
    private String message;

    /* Getters and Setters */

}
Kopiuj
@Entity
@Table(name = "categories")
public class Category {

    @Id
    @GeneratedValue
    @Column(name = "category_id")
    private Integer id;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "section_id")
    private CategorySection parent;

    @Column(nullable = false, length = 64)
    private String name;

    @Column(nullable = false, length = 128)
    private String description;

    @OneToMany(fetch = FetchType.EAGER)
    @JoinColumn(name = "category_id")
    private List<Topic> topics;

    /* Getters and Setters */

}

Chodzi o to? http://jaceklaskowski.pl/wiki/Testowanie_z%C5%82%C4%85cze%C5%84_FETCH_JOIN_w_JPA

Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
1

Tak, mniej więcej o to. Chodzi o to żebyś nie miał wszędzie fetch = FetchType.EAGER i żebyś miał kilka osobnych metod do pobierania tych samych obiektów, tylko że jedna metoda ma fetch join (i wyciąga jakieś powiązania z bazy) a inna nie ma.


"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
ZA
  • Rejestracja:około 10 lat
  • Ostatnio:prawie 5 lat
  • Postów:174
0

Ok, rozumiem. Odezwę się jak przeczytam artykuł i zmodyfikuję kod który mam :)

ZA
  • Rejestracja:około 10 lat
  • Ostatnio:prawie 5 lat
  • Postów:174
0

Przeczytałem wspomniany wyżej artykuł. W kodzie testowym występuje takie coś:

Kopiuj
        // Ilość kont przypisanych do Jacka to ilość_kont^ilość_użytkowników. Dlaczego?
        int iloscKont = 3, iloscOsob = 2;
        assert osoby.get(0).getKonta().size() == Math.pow(iloscKont, iloscOsob);

Znalazłem również inny artykuł http://piotrnowicki.com/2012/10/fetch-join-is-still-a-join/ Jest w nim napisane:

It’s very common problem with JOINs where each table from the left hand side is connected with more than one related rows in the right hand side table

Po przeczytaniu dalszej części artykułu, możemy zobaczyć że wzór ilość_kont^ilość_użytkowników jest błędny. Zamiast tego powinno być ilość_kont*ilość_użytkowników.

Zaimplementowałem to w swój kod i oczywiście mam problem z dublującymi wynikami. Zależy mi na zachowaniu ich kolejności więc nie mogę się zastosować do przedstawionej tutaj porady z setem http://stackoverflow.com/questions/18753245/one-to-many-relationship-gets-duplicate-objects-whithout-using-distinct-why Po użyciu DISTINCT wyniki nie dublują się, ale zastanawiam się czy takie rozwiązanie jest w porządku od strony technicznej.

No i pojawia się następne pytanie :)
Jeżeli chciałbym pokazywać najnowszy temat dla każdej z kategori to muszę robić kilka SELECT'ów, czy można to jakoś sprytnie zrobić?

shagrin
Może podaj zapytanie, które zwraca te duplikaty? Jeśli chodzi o najnowszy temat, zakładam że każdy temat ma datę utworzenia?
ZA
Z duplikatami SELECT c FROM CategorySection c JOIN FETCH c.categories bez duplikatów SELECT DISTINCT c FROM CategorySection c JOIN FETCH c.categories
ZA
  • Rejestracja:około 10 lat
  • Ostatnio:prawie 5 lat
  • Postów:174
0

No i co zrobić jeżeli potrzebuję tylko jedną stronę wyników z FETCH JOIN?

ZA
  • Rejestracja:około 10 lat
  • Ostatnio:prawie 5 lat
  • Postów:174
0

Chyba mam pewien pomysł :). Te wyniki dublują się po to, żebym mógł zrobić na nich setMaxResults w JPA?

shagrin
  • Rejestracja:prawie 17 lat
  • Ostatnio:ponad 6 lat
  • Lokalizacja:Norwegia, Stavanger
0

SELECT c FROM CategorySection c JOIN FETCH c.categories bez duplikatów SELECT DISTINCT c FROM CategorySection c JOIN FETCH c.categories

Pamiętaj, że ten DISTINCT nie jest na poziomie SQL a HQL. Oznacza to, że nie jest robiony na bazie, ale w pamięci, na pobranych danych. Wg. mnie paginacja wyników będzie działała zupełnie losowo.

Nie rozumiem do końca, jakie masz tam duplikaty - zapytanie źle łączy dane? Pojawiają się duplikaty po dodaniu nowych danych? Jak masz opisaną relację CategorySelections - Category?

Napisz sobie w SQL zapytanie, żeby pobrać te dane, które chcesz i sprawdź, czy to które masz w JPA faktycznie jest tym co chcesz.


ZA
  • Rejestracja:około 10 lat
  • Ostatnio:prawie 5 lat
  • Postów:174
0

Powiedzmy że z DISTINCT sobie poradziłem. Zastanawia mnie jak zrobić order i limit na wynikach fetch join

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)