#13.1 — Baza danych z JPA cz. 1

By 21 listopada 2014Kurs Javy

W dzisiejszej i jutrze­jszej lekcji omówimy pod­stawy JPA — stan­dar­du, który znacznie upraszcza obsługę bazy danych z poziomu aplikacji. W pier­wszej częś­ci lekcji zobaczymy sam JPA, w kole­jnej zaś nauczymy się uży­wać go we włas­nym pro­jek­cie.

JPA to stan­dard z grupy tzw. ORM (ang. Object-Rela­tion­al Map­ping), co w wol­nym tłu­macze­niu oznacza mapowanie z mod­elu obiek­towego (takiego, jaki uży­wamy w aplikac­jach Java) do mod­elu rela­cyjnego (takiego, z jakiego korzys­ta­ją bazy danych). Oczy­wiś­cie założe­niem jest, żeby pracę pro­gramisty uproś­cić, a nie utrud­nić, dlat­ego stara­no się zau­tomaty­zować jak najwięcej rzeczy.

Lekcja

Aby korzys­tać z JPA będziemy potrze­bowali tzw. providera — bib­liote­ki, która dostar­cza imple­men­tację stan­dar­du JPA (JPA to tylko zbiór kon­trak­tów, tzn. opisów, jakie funkc­je mają być real­i­zowane i w jaki sposób — sami może­my wybrać lub stworzyć imple­men­tację, która będzie się tym zaj­mowała)

Tak naprawdę aby korzys­tać z JPA będziemy potrze­bowali jed­nej adno­tacji na każdej klasie oraz jed­nej na jej polu (iden­ty­fika­torze), którą chce­my też prze­chowywać w bazie danych (tak, jed­nej!). Jed­nocześnie może­my (i będziemy) mody­fikować domyślne zachowanie JPA za pomocą dodatkowych adno­tacji aby powiedzieć dokład­nie, co chce­my, i w jaki sposób, żeby było zro­bione. To kole­jny przykład elasty­cznoś­ci i wygody, którą dosta­je­my dzię­ki pode­jś­ciu con­ven­tion-over-con­fig­u­ra­tion.

Adnotacje w JPA

W przy­pad­ku baz danych (jak pewnie pamięta­cie z ostat­niej lekcji) mówimy o enc­jach i tak najważniejszą adno­tacją w przy­pad­ku JPA jest właśnie @Entity. Adno­tac­ja ta nie wyma­ga żad­nych para­metrów (może­my wybrać nazwę — pewnego rodza­ju alias, uży­wany później w zapy­ta­ni­ach, ale zostaniemy przy domyśl­nym — nazwie klasy), umieszcza­my ją nad całą klasą, która ma być mapowana:

@Entity
public class Kot {
    @Id
    private Long id;

    //...
}

Klasa taka, którą adno­tu­je­my za pomocą @Entity, musi mieć pub­liczne get­tery i set­tery dla każdego pola. Założe­nia przyjęte przez JPA w takiej sytu­acji są następu­jące:

  • tabela nazy­wa się tak jak klasa
  • kolum­ny nazy­wa­ją się tak, jak pola i są odpowied­niego typu z możli­woś­cią wpisa­nia null

Powyżej widz­imy także adno­tację @Id — wskazu­je nam ona, że dane pole jest iden­ty­fika­torem (unikalnym) tego obiek­tu. Najczęś­ciej jest to pole typu Long o nazwie id lub podob­nej. Bard­zo częs­to moż­na spotkać się z adno­tacją @GeneratedValue, co wskazu­je, że iden­ty­fika­tor ten jest gen­erowany automaty­cznie w momen­cie zapisu do bazy danych.

Zmiana parametrów tabeli

Bard­zo częs­to mamy już gotową bazę danych, którą chce­my zmapować i nie chcąc zmieni­ać jej struk­tu­ry musimy dopa­sować nasze mapowanie (np. nazwę tabeli). Służy do tego adno­tac­ja @Table — najczęś­ciej wyko­rzys­tu­je­my jej para­metr name, jak na poniższym przykładzie:

@Entity
@Table(name="koty")
public class Kot {
    //...
}

Taki zapis spowodu­je, że klasa Kot będzie mapowana na tabelę ‘koty’ zami­ast domyśl­nej tabeli ‘Kot’

Zmiana parametrów kolumny

Podob­nie jak w przy­pad­ku tabeli, domyśl­ną nazwą kolum­ny jest nazwa pola. Najczęś­ciej jed­nak nie jest to zgodne z kon­wencją nazewnict­wa w bazie danych (gdzie częs­to zami­ast camel­Case uży­wamy nazw_z_podkreslnikami). Aby zmienić nazwę kolum­ny lub zmody­fikować jej atry­bu­ty może­my użyć adno­tacji @Column  jak na przykładzie poniżej:

@Entity
@Table(name="koty")
public class Kot {

    @Column(name="imie_kota", nullable=false)
    private String imieKota;
    //...
}

Widz­imy tutaj dwie rzeczy: po pier­wsze wskazu­je­my, że pole imieKo­ta będzie zapisy­wane w kolum­nie o nazwie imie_kota, na dodatek nie może mieć wartoś­ci NULL (ustaw­iliśmy atry­but nul­lable na false [domyśl­nie jest true] — jeśli spróbu­je­my zapisać w bazie danych obiekt, który w tym polu będzie miał wartość null, otrzy­mamy błąd bazy danych.

Uwa­ga! Bard­zo częs­to będziemy mieli do wyboru dwie adno­tac­je o takiej samej nazwie, np:

Jest to spowodowane tym, że frame­work doda­je nowe możli­woś­ci i opc­je. Zasad­nic­zo, o ile nie potrze­bu­je­my naprawdę czegoś, co ofer­u­je bib­liote­ka, powin­niśmy uży­wać adno­tacji z paki­etu javax.persistence, ponieważ są to adno­tac­je stan­dar­d­owe JPA — korzys­tanie tylko z tych adno­tacji poz­woli w przyszłoś­ci pod­mienić imple­men­tację JPA tylko za pomocą kon­fig­u­racji.
To ogól­na zasa­da, o ile możli­we, powin­niśmy korzys­tać ze stan­dard­ów a nie z ich rozsz­erzeń zawsze, kiedy jest to możli­we.

EntityManager

Enti­ty­Man­ag­er to stan­dar­d­owy sposób wykony­wa­nia oper­acji na obiek­tach w stan­dard­zie JPA. Jak zobaczymy w drugiej częś­ci lekcji, skon­fig­u­ru­je­my fab­rykę (czyli sposób, w jaki będą automaty­cznie twor­zone Enti­ty­Man­agery), dzię­ki czemu będziemy mogli uży­wać Enti­ty­Man­agerów w naszych klasach.
Na dzisi­aj ważne jest, żeby pamię­tać o tym, że Enti­ty­Man­ag­er to nasz ‘most’ pomiędzy bazą danych a naszą aplikacją.

EntityManager vs Session

Powód, dla którego porusza­my dzisi­aj ten tem­at (o tym, jak go uży­wać powiemy sobie jutro) jest związany z alter­naty­wnym sposobem, z którym może­my się spotkać w wielu tuto­ri­alach, a mianowicie Sesją (Ses­sion­Fac­to­ry i Ses­sion). Role Ses­sion oraz Enti­ty­Man­ag­er są podob­ne, Ses­sion ma nieco więk­sze możli­woś­ci, ale Enti­ty­Man­ag­er jest częś­cią stan­dar­du (Ses­sion nie). Ma tu zas­tosowanie zasa­da, o której wspom­i­nałem wcześniej — jeśli nie potrze­bu­je­my koniecznie funkcjon­al­noś­ci Ses­sion, uży­wa­jmy stan­dard­ów — Enti­ty­Man­agera. Szerzej na ten tem­at wypowiedzi­ał się Emmanuel Bernard, Architekt w zes­pole pracu­ją­cym nad Hiber­nate w krótkim wywiadzie , pod­sumowu­jąc w skró­cie moż­na posłużyć się cytatem: “We encour­age peo­ple to use the Enti­ty­Man­ag­er” .

W drugiej części

W drugiej częś­ci (już jutro) zobaczymy, jak dołączyć JPA do naszego pro­jek­tu i Springa oraz jak korzys­tać z JPA w naszych klasach DAO.

Licencja Creative Commons

Jeśli uważasz powyższą lekcję za przy­dat­ną, mamy małą prośbę: pol­ub nasz fan­page. Dzię­ki temu będziesz zawsze na bieżą­co z nowy­mi treś­ci­a­mi na blogu ( i oczy­wiś­cie, z nowy­mi częś­ci­a­mi kur­su Javy). Dzię­ki!

  •  
  •  
  •  
  •  
  •  
  • Jakub Zys­nars­ki

    Anno­tac­je, których używa­cie, są w pakiecie javax.persistence. Hiber­nate — w pakiecie org.hibernate.annotations posi­a­da inne — np. @Entity od wer­sji 4.0 jest jako dep­re­cat­ed. Jaka jest różni­ca, których anno­tacji uży­je­my?

    • Paki­et javax.persistence jest częś­cią stan­dar­du JPA. Hiber­nate miał własne ‘kopie’ adno­tacji z uwa­gi na to, że rozsz­erzał ich możli­woś­ci. Obec­nie te dodatkowe funkc­je są przenos­zone do zewnętrznych adno­tacji czy kon­fig­u­racji, przez co adno­tac­je stan­dar­d­owe w paki­etach hiber­nate sta­ją się zbędne. Doku­men­tac­ja także zale­ca korzys­tanie z tych dostęp­nych w pakiecie javax.persistence. Więcej infor­ma­cji z linka­mi zna­jdziesz też na stack­over­flow: http://stackoverflow.com/a/10352698/1563204 (właś­ci­wie całe pytanie tego doty­czy)

  • Dzię­ki, popraw­ione!

  • Krzysztof Ambrozi­ak

    Cześć

    W trak­cie lek­tu­ry zarówno tego blo­ga jak i specy­fikacji MySQL 5.7 i przykładów w sieci. Zauważyłem kil­ka rzeczy niewytłu­mac­zonych, których nie rozu­miem. Mam 3 pyta­nia odnośnie baz danych:
    PYTANIE 1. Wiedząc, że Java nie posi­a­da dla typów liczbowych specy­fika­to­ra unsigned, który wys­tępu­je w bazach danych np. MySQL, jak lep­iej tworzyć tabele z liczbową kolum­ną klucza głównego, która jest dodatkowo auto inkre­men­towana? Tworzyć ją tak:
    CREATE TABLE user (id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, …)
    i mieć możli­wość wstaw­ienia aż 4294967295 reko­rdów. Czy tak:
    CREATE TABLE user (id INT AUTO_INCREMENT PRIMARY KEY, …)
    Ujemne wartoś­ci dla takiej kolum­ny auto inkre­men­towanego klucza głównego da się wstaw­ić ale ręcznie bo kolum­na zdefin­iowana jako INT AUTO_INCREMENT z kluczem PRIMARY KEY i tak zacznie numerować reko­rdy od wartoś­ci 1.
    PYTANIE 2. Jak mapować typy całkow­itoliczbowe UNSIGNED w tabelach w bazie na typy całkow­itoliczbowe w klasach. Czy użyć najm­niejszego typu Javy który może pomieś­cić dany typ w bazie danych? Np. czy jeśli w jakiejś tabeli pew­na kolum­na jest typu TINYINT UNSIGNED, to w klasie odpowiadać jej ma pole typu short?
    PYTANIE 3. Zauważyłem, że podal­iś­cie @Id pri­vate Long id; Czy to jest jakaś kon­wenc­ja by typy liczbowe podawać jako typ obiek­towy “Long” zami­ast nieo­biek­towego “long”? Poza tym zauważyłem w innych przykładach, częs­to poda­je się w klasie właśnie typ Long. A jeśli jakaś tabela prze­chowywała­by, przykład­owo infor­ma­c­je o pier­wiastkach chemicznych, których jest póki co 118, to też trze­ba by podać typ Long? Typ Byte (nawet ze znakiem) wystar­czył­by w zupełnoś­ci.

    • Cześć, odpowieda­jąc na pyta­nia:
      1) Java ma typ ogól­no­liczbowy BigDec­i­mal i Big­In­te­ger, które mogą mieć dowol­ną pre­cyzję — JPA pozwala mapować także na takie typy, nie ma więc przeszkód, aby stosować mody­fika­tor unsigned na bazie danych. Zwróć uwagę, że także od definicji kolum­ny zależy fak­ty­czny zakres licz­by. W więk­szoś­ci przy­pad­ków rozważanie czy 2 mil­iardy reko­rdów wystar­czą czy nie jest jed­nak nieco na wyrost ;) zakres 2 mil­iardów w zupełnoś­ci wystar­czy, jeśli nie — zawsze możesz użyć typu BIGINT i Long w Javie
      2) Tak jak wspom­ni­ałem powyżej możesz użyć Big­In­te­ger, z pełny­mi tego kon­sek­wenc­ja­mi. Najczęst­szą prak­tyką jest jed­nak mapowanie, które podąża za domyśl­nym mapowaniem uży­wanej bazy danych, w przy­pad­ku MySQL zerknij tutaj: https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-reference-type-conversions.html (tabela 5.2). W przy­pad­ku kolum­ny typu INT wid­nieje tam infor­ma­c­ja: “java.lang.Integer, if UNSIGNED java.lang.Long”
      3) Korzys­tanie z typów Integer/Long jest po pros­tu wygodne — więk­szość oper­acji przyj­mu­je i zwraca tego rodza­ju dane, więc nie jest wyma­gana ich kon­wer­s­ja. Z punk­tu widzenia opty­mal­iza­cji mówimy o 8 baj­tach w przy­pad­ku typu Long — mając na uwadze pojem­noś­ci nośników i rozmi­ary pamię­ci aktu­al­nie dostęp­ne (a w Javie także pulę obiek­tów-liczb), w więk­szoś­ci aplikacji stosowanie typu Long nie ma żad­nego prak­ty­cznego wpły­wu na dzi­ałanie aplikacji a jest po pros­tu wygod­niejsze w kon­tekś­cie uży­wa­nia.
      Co do Long vs long — prymi­ty­wy mają domyśl­ną wartość 0 (w przy­pad­ku liczb), co może prowadz­ić do przeoczeń — dobrą prak­tyką jest uży­wanie obiek­tów, o ile nie ma szczegól­nych wyma­gań sugeru­ją­cych inne pode­jś­cie

      Mam nadzieję że to pomoże trochę rozwiać wąt­pli­woś­ci :)

      • Krzysztof Ambrozi­ak

        Tak dzięku­ję, i to bard­zo pomogłeś. Czyli jeśli dobrze zrozu­mi­ałem, to pro­jek­tu­jąc bazę danych i poszczególne tabele, najlepiej od razu ustaw­iać kolum­ny klucza głównego na INT lub LONG (bez UNSIGNED) bo w javowej aplikacji back­endowej jeśli będą innego typu numerycznego to i tak zostaną gdzieś przekon­wer­towane albo na Inte­ger albo na Long, zgadza się? Czyli nie ma potrze­by oszczędzać bajtów na typy TINYINT, SMALLINT czy MEDIUMINT w tabelach?

  • H

    Nie mogę czegoś tutaj zrozu­mieć. Sko­ro JPA jest stan­dar­d­em komu­nikacji aplikacji z bazą danych (stan­dard — spis znor­mal­i­zowanych zasad), potrze­bu­je­my więc dostaw­cy (providera), który zapewni nam ten stan­dard. Z jakiego więc providera korzys­tamy w tej lekcji (nigdzie nie mogłem się doczy­tać) ?. Wiem, że takim providerem jest np. Hiber­nate, ale tutaj nie uży­wamy Hiber­nate. Więc providerem w tej lekcji jest .….. bib­liote­ka javax.persistance ? Jest to bib­liote­ka czys­tej Javy EE czy Springa? Czym w takim razie różni się frame­work Hiber­nate od tej bib­liote­ki?

    • Cześć,
      JPA jest tak jak mówisz stan­dar­d­em, czyli spisem znor­mal­i­zowanych zasad. W przy­pad­ku Javy może to być nie tylko for­mal­ny opis i doku­men­tac­ja, ale także kod (w tym wypad­ku paki­et javax.persistence) — np. adno­tac­je czy inter­fe­jsy. W tym przy­pad­ku uży­wasz więc JPA (jako stan­dar­du ale też imple­men­tacji w Javie) do opisa­nia swoich encji, ale bib­lioteką, która ‘rozu­mie’ te adno­tac­je jest Hiber­nate