Započnite s hibernacijom

Dobro je razumjeti potrebu za objektnim / relacijskim mapiranjem (ORM) u Java aplikacijama, ali vjerojatno ste željni vidjeti Hibernate u akciji. Za početak ćemo vam pokazati jednostavan primjer koji pokazuje dio njegove snage.

Kao što vjerojatno znate, tradicionalno je da programska knjiga započinje primjerom "Hello World". U ovom poglavlju slijedimo tu tradiciju uvodeći Hibernate s relativno jednostavnim programom "Hello World". Međutim, jednostavno ispisivanje poruke na prozor konzole neće biti dovoljno za stvarno demonstriranje hibernacije. Umjesto toga, naš će program pohraniti novostvorene objekte u bazu podataka, ažurirati ih i izvršiti upite za njihov dohvat iz baze podataka.

Pored kanonskog primjera "Hello World", uvodimo osnovne Hibernate API-je i dajemo detalje za osnovnu konfiguraciju.

"Pozdrav svijetu" s hibernacijom

Hibernate aplikacije definiraju trajne klase koje se "preslikavaju" u tablice baze podataka. Naš primjer "Hello World" sastoji se od jedne klase i jedne datoteke za mapiranje. Pogledajmo kako izgleda jednostavna trajna klasa, kako je specificirano mapiranje i neke stvari koje možemo učiniti s primjerima trajne klase pomoću Hibernate.

Cilj našeg uzorka aplikacije je pohraniti poruke u bazu podataka i dohvatiti ih za prikaz. Aplikacija ima jednostavnu trajnu klasu Messagekoja predstavlja ove poruke za ispis. Naš je Messagerazred prikazan na popisu 1.

Popis 1. Message.java: Jednostavna trajna klasa

paket zdravo; javna klasa Poruka {private Long id; privatni tekst niza; privatna poruka nextMessage; privatna poruka () {} javna poruka (tekst niza) {this.text = text; } public Long getId () {return id; } private void setId (Long id) {this.id = id; } javni String getText () {return tekst; } javna void setText (tekst niza) {this.text = text; } javna poruka getNextMessage () {return nextMessage; } javna praznina setNextMessage (Poruka nextMessage) {this.nextMessage = nextMessage; }}

Naša Messageklasa ima tri atributa: atribut identifikator, tekst poruke i referencu na drugi Message. Atribut identifikator omogućuje aplikaciji pristup identitetu baze podataka - vrijednosti primarnog ključa - trajnog objekta. Ako dvije instance Messageimaju istu vrijednost identifikatora, predstavljaju isti redak u bazi podataka. Odabrali smo Longvrstu našeg atributa identifikatora, ali to nije uvjet. Hibernate omogućuje gotovo sve za vrstu identifikatora, kao što ćete vidjeti kasnije.

Možda ste primijetili da svi atributi Messageklase imaju metode pristupa svojstvima u stilu JavaBean. Klasa također ima konstruktor bez parametara. Uporne klase koje koristimo u našim primjerima gotovo će uvijek izgledati otprilike ovako.

Instance Messageklase može se upravljati (napravio uporan) po Hibernate, ali oni ne moraju biti. Budući da Messageobjekt ne implementira nijednu klasu ili sučelja specifična za Hibernate, možemo ga koristiti kao bilo koji drugi Java razred:

Poruka poruke = nova poruka ("Hello World"); System.out.println (message.getText ());

Ovaj fragment koda čini točno ono što smo očekivali od aplikacija "Hello World": ispisuje "Hello World"se na konzolu. Moglo bi izgledati kao da se ovdje trudimo biti slatki; zapravo demonstriramo važnu značajku koja razlikuje Hibernate od nekih drugih rješenja za trajnost, poput EJB (Enterprise JavaBean) entiteta. Naša trajna klasa može se koristiti u bilo kojem kontekstu izvršenja - nije potreban poseban spremnik. Naravno, ovdje ste došli vidjeti sam Hibernate, pa spremimo novo Messageu bazu podataka:

Sjednica sesije = getSessionFactory (). OpenSession (); Transakcija tx = session.beginTransaction (); Poruka poruke = nova poruka ("Hello World"); session.save (poruka); tx.commit (); session.close ();

Ovaj kod poziva hibernaciju Sessioni Transactionsučelja. (Uskoro ćemo doći do tog getSessionFactory()poziva.) Rezultat je izvršavanje nečega sličnog sljedećem SQL-u:

umetnuti u vrijednosti MESSAGES (MESSAGE_ID, MESSAGE_TEXT, NEXT_MESSAGE_ID) (1, 'Hello World', null) 

Držite se - MESSAGE_IDstupac se inicijalizira na čudnu vrijednost. Nismo nigdje postavili idsvojstvo message, pa bismo očekivali da jest null, zar ne? Zapravo, idsvojstvo je posebno: svojstvo je identifikatora - sadrži generiranu jedinstvenu vrijednost. (Razgovarat ćemo o načinu generiranja vrijednosti kasnije.) MessageHibernaciji vrijednost dodijeli Hibernate kada save()se pozove.

Za ovaj primjer pretpostavljamo da MESSAGEStablica već postoji. Naravno, želimo da naš program "Hello World" ispiše poruku na konzolu. Sad kad imamo poruku u bazi podataka, spremni smo to pokazati. Sljedeći primjer dohvaća sve poruke iz baze podataka po abecednom redu i ispisuje ih:

Sesija newSession = getSessionFactory (). OpenSession (); Transakcija newTransaction = newSession.beginTransaction (); Popis poruka = ​​newSession.find ("iz Poruka kao m poredak prema m.text asc"); System.out.println (messages.size () + "pronađena poruka:"); for (Iterator iter = messages.iterator (); iter.hasNext ();) {Poruka poruke = (Poruka) iter.next (); System.out.println (message.getText ()); } newTransaction.commit (); newSession.close ();

Doslovni niz "from Message as m order by m.text asc"je Hibernate upit, izražen Hibernateovim vlastitim objektno orijentiranim Hibernate Query Language (HQL). Ovaj se upit interno prevodi u sljedeći SQL kada find()se pozove:

odaberite m.MESSAGE_ID, m.MESSAGE_TEXT, m.NEXT_MESSAGE_ID iz MESSAGES m poredak prema m.MESSAGE_TEXT uzlazno 

Ispisuje se fragment koda:

Pronađena je 1 poruka: Hello World 

Ako nikada prije niste koristili ORM alat kao što je Hibernate, vjerojatno ste očekivali da ćete SQL izjave vidjeti negdje u kodu ili metapodacima. Oni nisu tamo. Sav SQL generira se u vrijeme izvođenja (zapravo prilikom pokretanja, za sve SQL izraze koji se mogu ponovno upotrijebiti).

Da bi se ta čarolija mogla dogoditi, Hibernate treba više informacija o tome kako Messagenastavu treba učiniti trajnom. Te se informacije obično daju u dokumentu za preslikavanje XML-a . Dokument mapiranja definira, između ostalog, kako se svojstva Messageklase preslikavaju na stupce MESSAGEStablice. Pogledajmo dokument mapiranja na popisu 2.

Popis 2. Jednostavno preslikavanje hibernacije XML


  

Dokument mapiranja govori hibernaciji da se Messageklasa treba održavati u MESSAGEStablici, da se svojstvo identifikatora preslikava u imenovani stupac MESSAGE_ID, da se svojstvo teksta preslikava u imenovani stupac MESSAGE_TEXTi da je imenovano nextMessagesvojstvo povezano s više-prema-jednom višestrukost koja se preslikava na stupac s imenom NEXT_MESSAGE_ID. (Za sada ne brinite o ostalim pojedinostima.)

Kao što vidite, XML dokument nije teško razumjeti. Jednostavno ga možete pisati i održavati ručno. Koju god metodu odabrali, Hibernate ima dovoljno podataka da u potpunosti generira sve SQL izraze koji bi bili potrebni za umetanje, ažuriranje, brisanje i dohvaćanje instanci Messageklase. Ne morate više pisati ove SQL izraze ručno.

Bilješka
Mnogi programeri Java žalili su se na "pakao metapodataka" koji prati razvoj J2EE. Neki su predložili prelazak s XML metapodataka natrag na običan Java kôd. Iako aplaudiramo ovom prijedlogu zbog nekih problema, ORM predstavlja slučaj kada su metapodaci temeljeni na tekstu zaista neophodni. Hibernate ima razumne zadane vrijednosti koje smanjuju tipkanje i definiciju zrelog tipa dokumenta koja se može koristiti za automatsko dovršavanje ili provjeru valjanosti u uređivačima. Možete čak i automatski generirati metapodatke pomoću različitih alata.

Sada, promijenimo svoju prvu poruku i, dok je već u tome, stvorimo novu poruku povezanu s prvom, kao što je prikazano na popisu 3.

Popis 3. Ažuriranje poruke

Sjednica sesije = getSessionFactory (). OpenSession (); Transakcija tx = session.beginTransaction (); // 1 je generirani id prve poruke Message message = (Message) session.load (Message.class, new Long (1)); message.setText ("Pozdrav Zemljane"); Poruka nextMessage = nova poruka ("Vodi me do svog vođe (molim)"); message.setNextMessage (nextMessage); tx.commit (); session.close ();

Ovaj kod poziva tri SQL izraza unutar iste transakcije:

odaberite m.MESSAGE_ID, m.MESSAGE_TEXT, m.NEXT_MESSAGE_ID iz MESSAGES m gdje m.MESSAGE_ID = 1 umetnite u MESSAGES (MESSAGE_ID, MESSAGE_TEXT, NEXT_MESSAGE_ID) vrijednosti (2, 'Vodi me do svog vođe (molimo)', null) ažuriraj MESS postavite MESSAGE_TEXT = 'Pozdrav zemljani', NEXT_MESSAGE_ID = 2 gdje je MESSAGE_ID = 1 

Primijetite kako je Hibernate otkrio izmjenu texti nextMessagesvojstva prve poruke i automatski ažurirao bazu podataka. Iskoristili smo značajku hibernacije koja se naziva automatska provjera prljavštine : ova nam značajka štedi napor da izričito tražimo od hibernacije da ažurira bazu podataka kada modificiramo stanje objekta unutar transakcije. Slično tome, možete vidjeti da je nova poruka postala postojana kad je referenca kreirana iz prve poruke. Ova se značajka naziva kaskadno spremanje : štedi nam napor da se novi objekt eksplicitno učini trajnim pozivanjemsave(), sve dok je već postojana instanca dostupna. Također primijetite da redoslijed SQL izraza nije isti kao redoslijed kojim postavljamo vrijednosti svojstava. Hibernate koristi sofisticirani algoritam kako bi odredio učinkovito naručivanje koje izbjegava kršenja ograničenja stranog ključa baze podataka, ali je i dalje dovoljno predvidljivo za korisnika. Ova se značajka naziva transakcijsko zapisivanje .

Ako ponovno pokrenemo "Hello World", ispisuje se:

Pronađene su 2 poruke: Pozdrav Zemljani Odvedi me do svog vođe (molim) 

Ovo je onoliko koliko ćemo uzeti aplikaciju "Hello World". Sad kad napokon imamo nešto koda, vratit ćemo se korak unatrag i predstaviti pregled glavnih API-ja Hibernate-a.

Razumijevanje arhitekture

The programming interfaces are the first thing you have to learn about Hibernate in order to use it in the persistence layer of your application. A major objective of API design is to keep the interfaces between software components as narrow as possible. In practice, however, ORM APIs aren't especially small. Don't worry, though; you don't have to understand all the Hibernate interfaces at once. The figure below illustrates the roles of the most important Hibernate interfaces in the business and persistence layers.

We show the business layer above the persistence layer, since the business layer acts as a client of the persistence layer in a traditionally layered application. Note that some simple applications might not cleanly separate business logic from persistence logic; that's okay—it merely simplifies the diagram.

The Hibernate interfaces shown in the figure above may be approximately classified as follows:

  • Interfaces called by applications to perform basic CRUD (create/read/update/delete) and querying operations. These interfaces are the main point of dependency of application business/control logic on Hibernate. They include Session, Transaction, and Query.
  • Interfaces called by application infrastructure code to configure Hibernate, most importantly, the Configuration class.
  • Callback interfaces that allow the application to react to events occurring inside Hibernate, such as Interceptor, Lifecycle, and Validatable.
  • Interfaces that allow extension of Hibernate's powerful mapping functionality, such as UserType, CompositeUserType, and IdentifierGenerator. These interfaces are implemented by application infrastructure code (if necessary).

Hibernate makes use of existing Java APIs, including JDBC (Java Database Connectivity), Java Transaction API (JTA), and Java Naming and Directory Interface (JNDI). JDBC provides a rudimentary level of abstraction of functionality common to relational databases, allowing almost any database with a JDBC driver to be supported by Hibernate. JNDI and JTA allow Hibernate to be integrated with J2EE application servers.

U ovom odjeljku ne pokrivamo detaljnu semantiku Hibernate API metoda, već samo ulogu svakog od primarnih sučelja. Većinu ovih sučelja možete pronaći u paketu net.sf.hibernate. Kratko ćemo pogledati svako sučelje zauzvrat.

Jezgra sučelja

Pet osnovnih sučelja koristi se u gotovo svakoj aplikaciji hibernacije. Korištenjem ovih sučelja možete pohraniti i dohvatiti trajne objekte i kontrolirati transakcije.

Sučelje sesije