Upload
cbyte84
View
261
Download
15
Embed Size (px)
DESCRIPTION
JPA sa fokusom na hibernate, priručnik za rad sa bazama podataka pomocu jave i orm - tehnologije.
Citation preview
Java Persistence API sa fokusom na Hibernate v1.0 draft
2
Predgovor
U ovom priručniku je predstavljena JPA 2.0 specifikacija (Java Persistence API) i njena implementacija sa Hibernate
framework-om, koji zajedno čine za programski jezik Java jedan od najpopularniji pristupa za mapiranje objektno-
orijentisanog modela sa relacionom bazom podataka, poznato kao Object-Relational Mapping (ORM).
Ideja i nastanak ovog priručnika su inspirisani na više godišnjem radu autora sa studentima na Fakultetu
informacijskih tehnologija Univerziteta "Džemal Bijedić" u Mostaru na predmetu Softverski inženjering. Želja
autora je da, pored materijala koji bi unaprijedio nastavni proces na navedenom predmet, omogući i širem krugu
zainteresovanih za ovu oblast pregled i dostupnost materijala na bosanskom jeziku.
Pored izlaganja glavnih koncepata JPA tehnologije autor je u priručniku kroz primjernu mnogobrojnih praktičnih
primjera pokušao olakšati usvajanje prezentovane materije vodeći se principom što uradim to znam.
Ovaj rad je predstavljen u tri cjeline i to, u prvom dijelu je dat je opis JDBC drajvera, načini uspostavljanja
komunikacije sa DBMS-om i izvršavanje SQL izraza bez korištenja ORM alata.
U drugom dijelu je opisana JPA specifikacija za označavanje klasa i njenih članova, te način uspostavljanja
asocijacije i nasljeđivanja između klasa. Opisan je životni vijek entitetskih objekata za operacije uzimanja,
modifikovanja i brisanja iz baze podataka. U istom poglavlju opisan je jezik JPQL (Java Persistence Query Language)
koji predstavlja objektno orijentisanu varijantu SQL-a.
U trećem dijelu je ukratko opisan Hibernate framework sa svojim specifičnostima u odnosi na druge JPA
provajdere. Prikazane su glavne razlike u korištenju ovog frameworka kroz JPA 2.0 specifikaciju i vlastitu Hibernate
specifikaciju koja se još uvijek popularna kod mnogih programera.
Za uspješno praćenje navedene materije očekuje se osnovno znanje iz programskoj jezika Java te minimalno
iskustvo u radu relacionim bazama podataka.
Primjedbe, sugestije i komentari su uvijek dobrodošli. Kontakt e-mail adresa je [email protected].
Autor
Java Persistence API sa fokusom na Hibernate v1.0 draft
3
Sadržaj
1. JDBC 5
1.1 Connection URL 6
1.2 Driver class 7
1.3 Primjer ostvarivanja konekcije 7 1.3.1 Statement 7 1.3.2 PreparedStatement 8 1.3.3 CallableStatement 8 1.3.4 ResultSet 8 1.3.5 Kompletan primjer 10
1.4 Napomene za MySql 11
1.5 Napomene za Microsoft SQL Server 11 1.5.1 Windows autentifikacija 11 1.5.2 SQL Server autenfikacija 12 1.5.3 TCP/IP protocol 13
2. Java annotations 15
3. JPA 16
3.1 Entity 16 3.1.1 Imenovanje tabele 17
3.2 Field 17 3.2.1 Označavanje 17 3.2.2 Primarni ključ 17 3.2.3 Generisanje vrijednosti primarnog ključa 18 3.2.4 Ignorisanje članova (Transient) 18 3.2.5 Imenovanje kolona 18 3.2.6 Dužina stringova 18 3.2.7 Dozvola null-vrijednosti 19 3.2.8 Temporal 19 3.2.9 Lob 19 3.2.10 Zadatak 20
3.3 Asocijacije 20 3.3.1 OneToOne 20 3.3.2 ManyToOne i OneToMany 24 3.3.3 ManyToMany 27
3.4 Nasljeđivanje 28 3.4.1 Single table mapping 28 3.4.2 Joined-tables mapping 29
3.5 Entity Manager 29 3.5.1 JPA postavke 30 3.5.2 EntityManagerFactory 30 3.5.3 Instanciranje EntityManager-a 31 3.5.4 EntityTransacion 31 3.5.5 Rollback 31 3.5.6 Dodavanje objekata u bazu podataka 32
Java Persistence API sa fokusom na Hibernate v1.0 draft
4
3.5.7 Uzimanje objekata iz baze podataka 34 3.5.8 Modifikacija objekata u bazi podataka 37 3.5.9 Brisanje objekata iz baze podataka 38 3.5.10 Moguće promjene stanja entity-objekata 40
3.6 JPQL – Java Persistence Query Language 40 3.6.1 Ugrađene funkcije 41 3.6.2 Relacije 42 3.6.3 Višestruki SELECT izrazi 43 3.6.4 JPQL primjeri 43 3.6.5 Izvršavanje JPQL 44 3.6.6 Izvršavanje SQL-a 45 3.6.7 Korištenje parametara 45
3.7 JPA provajderi 46
4. Hibernate 47
4.1 Biblioteke 47
4.2 Konfiguracija 47
4.3 Hibernate izvan JPA specifikacije 50
Literatura 52
Popis tabela 52
Popis slika 52
Java Persistence API sa fokusom na Hibernate v1.0 draft
5
1. JDBC
JDBC (Java Database Connectivity) predstavlja skup API-a (application programming interface) za programski jezik
Java koji definira način pristupa bazi podataka. Sadrži skup metoda za otvaranje konekcije, izvršavanje upita,
pregled i izmjenu podataka. JDBC klase su grupisane u pakete (packages) "java.sql.*" i "javax.sql.*".
Za pristup određenom DBMS-u (npr. MySQL, MS SQL Server) potreban je odgovarajući JDBC driver koji
implementira potrebne API funkcije. JDBC drajveri su analogija za ODBC drivers i ADO.NET data providers.
Postoji veliki broj JDBC drajvera koji se mogu grupisani prema tipu (type 1, 2, 3 i 4). JDBC Type 4 drivers (definisani
2006.) predstavljaju implementaciju API za direktni pristup bazi podataka putem mrežnog protokola i to bez
middleware posrednika (kao što je to slučaj za type 3 drajvere). JDBC drajver se obično sastoji od jednog JAR fajla
koji specifičan za određeni DBMS.
Neki od najčešće korištenih drajvera su dati u tabeli 1.1.
DBMS Oficijelni JDBC drajver Naziv fajla
MySQL http://dev.mysql.com/downloads/connector/j/ mysql-connector-java-5.x.x-bin.jar
PostgreSQL http://jdbc.postgresql.org/download.html postgresql-9.x-x.jdbc4.jar
Microsoft
SQL Server http://msdn.microsoft.com/en-us/sqlserver/aa937724
sqljdbc4.jar
Windows autentifikacija1
(opcionalno)
x86\sqljdbc_auth.dll
x64\sqljdbc_auth.dll
IA64\sqljdbc_auth.dll
Oracle DB http://www.oracle.com/technetwork/database/enterprise-
edition/jdbc-112010-090769.html ojdbc6.jar
IBM DB2 http://www-
933.ibm.com/support/fixcentral/swg/doSelectFixes?options
.selectedFixes=DSClients--jdbc_sqlj-9.7.0.5-FP005
db2jcc4.jar
Tabela 1.1. JDBC drajveri za različite DBMS-ove
Potrebno preuzeti odgovarajući JAR fajla i dodati ga u class-path Java projekta.
DBMS se može nalaziti nalazi na lokalnom računaru ili udaljenom računaru. U oba slučaja pristup se vrši putem
TCP/IP protokola.
1Platformski ovisno: Windows autentfikacija je moguća samo ukoliko se Java aplikacija izvršava za Windows-u.
Java Persistence API sa fokusom na Hibernate v1.0 draft
6
1.1 Connection URL
Za pristup bazi podataka potrebno je poznavati slijedeće informacije:
Vrsta informacije Primjeri
adresa servera localhost
192.16.0.2
db.nekadomena.ba
broj porta
(ukoliko se ne koristi defaultni)
3306 (MySql Server)
1433 (Microsoft SQL Server
U koliko se na računaru nalaze dvije instance onda one ne mogu koristiti isti port.
ime baze Pubs
AdventureWorks
JDBC driver specifične opcije Windows autentifikacija za Microsoft SQL Server (da ili ne)
korisničko ime
(ukoliko se koristi)
root (MySql Server)
sa (Microsoft SQL Server)
lozinka
(ukoliko se koristi)
Tabela 1.2. Neophodne informacije za ostvarivanje konekcije
Na osnovu DBMS-a, adrese servera, broja porta, imena baze i specifičnih opcija za odabrani JDBC drajver potrebno
formirati Connection URL (ekvivalent Connection String-u u .NET-u) koji JDBC Connection Manager koristi za
ostvarivanje konekcije.
Svaki JDBC driver ima vlastiti format za Connection URL. U tabeli 1.3. je dato nekoliko primjera sa defaultnim
opcijama.
DBMS Primjer Connection URL-a (sa defaultnim portom)
MySQL jdbc:mysql://localhost/imeBaze
PostgreSQL jdbc:postgresql://localhost/imeBaze
Microsoft SQL Server jdbc:sqlserver://localhost;databaseName=imeBaze;integratedSecurity=true
Oracle DB jdbc:oracle:thin:[user/password]@[host]:SID
IBM DB2 jdbc:db2://localhost/imeBaze
Tabela 1.3. Connection URL formati za različite DBMS-ove
Broj porta se u Java aplikacijama obično navodi poslije adrese servera odvojen znakom dvotačke (:), dok u .NET
aplikacijama se koristi znak zareza (,).
Primjer navođenja porta u Connection URL-u za Microsoft SQL Server.
jdbc:sqlserver://localhost:1433;databaseName=imeBaze;integratedSecurity=true
Primjer navođenja porta u .NET Connection String-u za Microsoft SQL Server (nova sintaksa).
Java Persistence API sa fokusom na Hibernate v1.0 draft
7
Data Source=localhost,1433;Initial Catalog=imeBaze;Integrated Security=SSPI;
Primjer navođenja porta u .NET Connection String-u za Microsoft SQL Server (stara sintaksa).
Server=localhost,1433;Database=imeBaze;Trusted_Connection=True;
1.2 Driver class
Pored Connection URL-a u nekim slučajevima je potrebno poznavati klasu koja će se koristiti kao drajver (to je klasa
koja implementira interfejs java.sql.Driver) kako bi se mogla registrovati u JDBC Connection Manager.
Za JDBC 4 drajvere (Type 4) ova registracija se vrši automatski prilikom učitavanja JAR fajla tako da nije potrebno
vršiti ručnu registraciju. Svi drajveri iz tabele 1.4. su Type 4.
Iako većina ima plugin alata ima mogućnost pretraživanja drajver klasa unutar JAR fajla, ipak postoje neki alati gdje
je potrebno ručno navest klasu koja implementira potrebni drajver za pristup bazi podataka. Stoga su u tabeli 1.5.
navedene klase koje Connection Manager koristi se za ostvarivanje konekcije.
DBMS Driver class JAR fajl koji sadrži Driver class
MySQL com.mysql.jdbc.Driver mysql-connector-java-5.x.x-bin.jar
PostgreSQL org.postgresql.Driver postgresql-9.x-x.jdbc4.jar
Microsoft SQL Server com.microsoft.sqlserver.jdbc.SQLServerDriver sqljdbc4.jar
Oracle DB oracle.jdbc.OracleDriver ojdbc6.jar
IBM DB2 COM.ibm.db2.jdbc.app.DB2Driver db2jcc4.jar
Tabela 1.4. Klase koje implementiraju JDBC drajvere
1.3 Primjer ostvarivanja konekcije
Konekcija se ostvaruje pozivom funkcije DriverManager.getConnection. S obzirom da funkcija baza izuzetak,
potrebno je postaviti try-catch blok koji hvata izuzetak tipa SQLException.
public static void main(String[] args) { try { // java.sql.Connection; Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/imeBaze", "korIme", "loznika"); // ovdje dodati code za pristup bazi podataka // Važno: Nemojte zaboraviti zatvoriti konekciju conn.close(); } catch (SQLException e) { System.out.println("greška u ostvarivanju konekcije: " + e.getMessage()); } }
1.3.1 Statement
Za izvršavanja SQL statement-a koji vrše dodavanje, izmjenu i brisanje podataka bez parametara može se koristiti
Java Persistence API sa fokusom na Hibernate v1.0 draft
8
klasa java.sql.Statement.
Statement stmt = conn.createStatement(); stmt.executeUpdate( "INSERT INTO Student( ime, prezime ) VALUES ( 'A', 'B' )" ); stmt.close();
1.3.2 PreparedStatement
Za izvršavanja SQL statement-a koji vrše dodavanje, izmjenu i brisanje podataka sa parametrima može se koristiti
klasa java.sql.PreparedStatement.
PreparedStatement stmt = conn.prepareStatement("INSERT INTO Student ( ime, prezime ) VALUES ( ?, ? )"); stmt.setString(1, "A"); stmt.setString(2, "B"); stmt.executeUpdate(); stmt.close();
Indeks pozicije počinju od 1, a ne od 0.
1.3.3 CallableStatement
Za izvršavanje procedura može se koristiti klasa java.sql.CallableStatement.
Primjeri procedura, koje dodaju jedan zapis u tabelu Student (bez povratne vrijednosti), su dati u nastavku.
MySql procedura:
CREATE PROCEDURE proc_DodajStudenta
(
p1 varchar(45),
p2 varchar(45)
)
BEGIN
insert into Student (ime, prezime) values (p1, p2);
END
Microsoft SQL Server procedura:
CREATE PROCEDURE proc_DodajStudenta
(
@p1 varchar(45),
@p2 varchar(45)
)
AS
BEGIN
insert into Student (ime, prezime) values (@p1, @p2);
END
Procedura se može pozvati na slijedeći način.
CallableStatement stmt = conn.prepareCall("{call proc_DodajStudenta (?, ?)}"); stmt.setString(1, "A"); stmt.setString(2, "B"); stmt.execute(); stmt.close();
1.3.4 ResultSet
Ukoliko je potrebno da Statement, PreparedStatement ili CallableStatement vrate vrijednosti (na osnovu select
izraza) onda se može koristit funkcija executeQuery.
Povratna vrijednost funkcije executeQuery jeste objekat klase ResultSet. Slijede primjeri.
Java Persistence API sa fokusom na Hibernate v1.0 draft
9
1.3.4.1 Statement
Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("select * from Student"); //pristup resultSet-u rs.close();
1.3.4.2 PreparedStatement
PreparedStatement stmt = conn.prepareStatement("select * from Student where Ime like ?"); stmt.setString(1, "A%"); ResultSet rs = stmt.executeQuery();
//pristup resultSet-u rs.close();
1.3.4.3 CallableStatement
MySql procedura je data u nastavku.
CREATE PROCEDURE proc_PronadjiStudente
(
p1 varchar(45)
)
BEGIN
select * from Student where ime like p1;
END
Microsoft SQL Server procedura je data u nastavku.
CREATE PROCEDURE proc_PronadjiStudente
(
@p1 varchar(45)
)
as
BEGIN
select * from Student where ime like @p1;
END
Poziv procedure proc_PronadjiStudente koja vraća ResultSet.
CallableStatement stmt = conn.prepareCall("{call proc_PronadjiStudente (?)}"); stmt.setString(1, "A%"); ResultSet rs = stmt.executeQuery();
//pristup resultSet-u
rs.close();
1.3.4.4 Pristup ResultSet-u
Objekat ResultSet sadrži pokazivač na trenutni red. Pomjerane pokazivača se vrši pomoću funkcije next. Funkcija
next vraća false ukoliko je pointer došao na kraj (zadnji red).
while (rs.next()) { System.out.print(rs.getInt(1) + "\t"); System.out.print(rs.getString(2) + "\t"); System.out.print(rs.getString(3) + "\n"); }
Pristup kolonama se vrši pomoću rednog broja kolone (počevši od 1) ili pomoću naziva kolone kao što je to
prikazano u narednom primjeru.
Java Persistence API sa fokusom na Hibernate v1.0 draft
10
while (rs.next()) { System.out.print(rs.getInt("id") + "\t"); System.out.print(rs.getString("ime") + "\t"); System.out.print(rs.getString("prezime") + "\n"); }
1.3.5 Kompletan primjer
Slijedi kompletan primjer aplikacije.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
public static void main(String[] args) { try { Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/baza1", "root", "test"); CallableStatement stmt = conn.prepareCall("{call proc_PronadjiStudente (?)}"); stmt.setString(1, "A%"); ResultSet rs = stmt.executeQuery(); while (rs.next()) { System.out.print(rs.getInt(1) + "\t"); System.out.print(rs.getString(2) + "\t"); System.out.print(rs.getString(3) + "\n"); } rs.close(); stmt.close(); conn.close(); } catch (SQLException e) { System.out.println("greška: " + e.getMessage()); } }
Prethodni primjer sadrži jedan nedostatak, koji je opisan u nastavku. Ako se konekcija uspješno ostvari (linija br. 5)
a ukoliko se desi greška prilikom izvršavanja upita (linija br. 6 ili 8) tada se baca izuzetak i preskače se ostatak try-
bloka (uključujući liniju 18, tj. conn.close) te izvršava se catch-block (linija br. 22). Znači, ostvarena konekcija će
ostati nezatvorena.
Slijedi dorađen primjer u kojem se close-funkcije nalazi u posebnom try-catch blok (koji je neovisan o prvom).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
public static void main(String[] args) { Connection conn = null; try { conn = DriverManager.getConnection("jdbc:mysql://localhost/baza1", "root", "test"); CallableStatement stmt = conn.prepareCall("{call proc_PronadjiStudente (?)}"); stmt.setString(1, "A%"); ResultSet rs = stmt.executeQuery(); while (rs.next()) { System.out.print(rs.getInt(1) + "\t"); System.out.print(rs.getString(2) + "\t"); System.out.print(rs.getString(3) + "\n"); } rs.close(); stmt.close(); } catch (SQLException e) {
Java Persistence API sa fokusom na Hibernate v1.0 draft
11
22 23 24 25 26 27 28 29 30
System.out.println("greška: " + e.getMessage()); } try { conn.close(); } catch (SQLException e) { } }
1.4 Napomene za MySql
Na MySQL se možete konektovati preko TCP/IP protokola koristeći korisnički nalog "root" i definisanu lozinku.
Ukoliko niste imali priliku definisati lozinu onda pokušajte sa praznom lozinkom (password = "").
Na linku http://dev.mysql.com/downloads/mysql/ možete preuzeti MySQL Community Server koji se sastoji od
klijentskog dijela i serverskog dijela MySQL-a.
Također, mnogi web server paketi za PHP (kao što su WAMP, XAMPP i dr.) u sebi sadrže klijentski dio (kroz web
interejs) i serverski dio MySQL-a.
U nastavku su nabrajani neki MySQL klijentski alata koji se mogu koristiti:
MySQL Workbench (u sklopu MySQL Community Server)
phpMyAdmin (dođe uz WAMP i neke druge alate)
HeidiSQL
Toad for MySQL
SQLyog
DbVisualizer
1.5 Napomene za Microsoft SQL Server
1.5.1 Windows autentifikacija
Windows autentfikacija je moguća samo ukoliko Java aplikacija izvršava na Windows OS-u. Microsoftov JDBC
drajver za Windows autentifikaciju zahtijeva dodatne biblioteke koje se nalaze u fajlu sqljdbc_auth.dll.
Postoje tri verzije sqljdbc_auth.dll fajla (x86, x64, IA64). Ove verzije su isključivo vezane za verziju JVM na
klijentskom računaru a ne za instaliranu verziju SQL Servera koji se može nalaziti na istom ili drugom računaru.
Npr. Ukoliko se koristi JRE 32 bit na Windows 64 potrebno odabrati x86\sqljdbc_auth.dll
Npr. Ukoliko se koristi JRE 64 bit na Windows 64 potrebno odabrati x64\sqljdbc_auth.dll
Kada se pokrene Java aplikacija (i ukoliko je uključena Windows autenfikacija) Microsoftov JDBC driver traži fajl
sqljdbc_auth.dll na sljedećim lokacijama:
1) root folder aplikacije ili Eclipse-projekta
2) PATH koji naveden kao "Environment variables", npr. c:\Windows itd.
3) <JVM folder>\bin
Ukoliko se ne pronađe fajl, pojavit će se greška sa porukom da drajver nije konfigurisan za Windows autentifikaciju.
Preporuka za Windows 32 bit: Kopirajte sqljdbc_auth.dll u <JRE folder>\bin i/ili <JDK folder>\bin.
fajl x86 \ sqljdbc_auth.dll možete kopirati u c:\program files\java\jdk_1.7x\bin
fajl x86 \ sqljdbc_auth.dll možete kopirati u c:\program files\java\jre_7\bin
Java Persistence API sa fokusom na Hibernate v1.0 draft
12
Preporuka za Windows 64 bit: Kopirajte odgovarajuće dll fajlove u oba <JRE folder>\bin (32 bit i 64 bit) i/ili <JDK
folder>\bin (32 bit i 64 bit) tako da ne morate voditi brigu kada prelazite između JVM 32 ili JVM 64.
fajl x64 \ sqljdbc_auth.dll možete kopirati u c:\program files\java\jdk_1.7x\bin
fajl x64 \ sqljdbc_auth.dll možete kopirati u c:\program files\java\jre_7\bin
fajl x86 \ sqljdbc_auth.dll možete kopirati u c:\program files (x86)\java\jdk_1.7x\bin
fajl x86 \ sqljdbc_auth.dll možete kopirati u c:\program files (x86)\java\jre_7\bin
Ukoliko je na Windowsu Vista ili 7 uključen UAC (User Account Control) onda će se sve aplikacije pokrenute od
strane administratora ponašati kao da su pokrenute sa ograničenim korisnikom (korisnik koji nije administrator na
operativnom sistemu) i to sve dok se ne pojavi UAC Dialog preko kojeg se mogu dozvoliti veće permisije (slika 1.1).
Međutim, ukoliko se pokrenete Sql Server Menagment Studio ili neka Java aplikacija, UAC dialoški prozor se neće
pojaviti a program će biti pokrenut u modu ograničenog korisnika. Ako se sa tako pokrenutom aplikacijom
(Managment Studio ili Java aplikacija) pokuša spojiti na bazu podataka koristeći Windows autenfikaciju pojavit će
se greška (ukoliko korišteni Windows korisnički nalog nije postavljen kao System administrator Sql Servera). U tom
slučaju imate dvije mogućnosti:
a) trajno deaktivirati UAC (slika 1.2)
b) dodijeliti Windows korisničkom nalogu odgovarajuće permisije na SQL Serveru (npr. sysadmin role)
Dodjeljivanje uloge sysadmin (System adminstrator SQL Servera) se može izvršiti na dva načina:
1. Prilikom instaliranja SQL Servera (slika 1.4)
2. Izmjenama postavki korisničkih naloga na SQL Serveru koristeći alat Sql Server Managment Studio (slika
1.3). Prethodno je potrebno alat pokrenuti u admin-modu (slika 1.3).
Slika 1.2. Isključen UAC (Win 7)
Slika 1.3. Windows korisnički nalog kao sysadmin na SQL Serveru
1.5.2 SQL Server autenfikacija
Pored Windows autenfikacije (koja je moguća samo na Windows
operativnom sistemu) može se koristiti i SQL Server autenfikaciju. To
znači korisnik se autentificira sa posebno kreiranim korisničkim
nalogom na SQL Serveru ili sa nekim od postojećih naloga (npr. System
administrator - korisničko ime „sa“).
Prilikom instaliranja SQL Servera može se birat da li će biti uključena
SQL Server autentfikacija (slika 1.4). Postavljena lozinka se dodjeljuje
Slika 1.1. UAC dialoški prozor
Java Persistence API sa fokusom na Hibernate v1.0 draft
13
postojećem korisniku „sa“. Ukoliko nije odabrana SQL Server autentifikacija, korisnički nalog „sa“ će biti
deaktiviran.
Slika 1.4. Sql Server autentifikacija + Windows autentifikacija
Uključivanje SQL Server autentifikacije može se vršiti, također, nakon instalacije (slika 1.5.). Potom je potrebno
dodati novi SQL Server korisnički nalog ili aktivirati neki postojećih (slika 1.6.).
Slika 1.5. Aktiviranje SQL autenfikacije na već
instaliranom SQL Serveru
Slika 1.6. Aktiviranje System administratora na SQL
Serveru (korisnik "sa")
1.5.3 TCP/IP protocol
Windows aplikacije (.NET i druge) mogu sa SQL serverom komunicirati koristeći četiri različita protokola.
Naziv protokola Konekcija na lokalni server Defaultne
postavke
Shared Memory
(samo lokalno)
localhost
(local)
.
.\imeInstance
uključen
Named Pipes \\.\pipe\sql\query uključen
TCP/IP tcp:localhost
localhost,1433
isključen
Java aplikacije (JDBC drajveri) mogu sa SQL Serverom komunicirati isključivo koristeći TCP/IP protokol, koji je po
Java Persistence API sa fokusom na Hibernate v1.0 draft
14
defaultu isključen. S toga potrebno je uključiti protokol (za instancu koju želimo koristit) pomoću alata „Sql Server
Configuration Manager“ (slika 1.7.) te provjeriti broj porta (slika 1.8.).
Slika 1.7. Protokoli za SQL Server
Slika 1.8. Broj porta za TCP/IP protokol na SQL Serveru
Koristian savjet: Prije uspostavljana konekcije na SQL Servera sa Java aplikacijom testirajte konekciju na SQL Server
preko Sql Server Managment Studio-u koristeći TCP/IP protokol (adresa: localhost,1433). Na ovaj način ćete
biti uvjereni da, u slučaju problema oko ostvarivanja konekcije, grešku morate tražiti u JDBC postavka a ne u Sql
Server konfiguraciji.
Slika 1.9. Testiranje pristupa na SQL Server putem TCP/IP protokola
Java Persistence API sa fokusom na Hibernate v1.0 draft
15
2. Java annotations
S obzirom da je za čitanje poglavlja JPA bitno poznavati šta su Java anotacije i kako se mogu koristiti, u nastavku su
dati neki neophodni detalji.
Java annotations predstavljaju sintaktičke metapodatke koji se mogu dodavati u Java kôd. Ovi metapodaci se mogu
koristiti za dodatno opisivanje klasa, funkcija, članova i paketa.
Primjer kreiranja jedne vlastite anotacije je dat u nastavku.
Prvo je neophodno u jednom Java fajlu2 (*.java) definisati anotaciju. Koristi se ključna riječ @interface. Slijedi
primjer definisanja anotacije MojKomentar sa četiri atributa (pouzdan, kreativnost, predznanje i nadimak). Atribut
pouzdan za razliku od ostalih ima postavljenu defaultnu vrijednost.
public @interface MojKomentar { boolean pouzdan() default false; int kreativnost(); int predznanje(); String nadimak(); }
Nakon definisanja vlastite anotacije MojKomentar, sada je moguće varijable, klase, funkcije i pakate označavati sa
ovom anotacijom u zavisnosti da li je dodata iznad varijable, klase, funkcije ili paketa. Primjer označavanja tri
varijable tipa Student je dat u nastavku.
@MojKomentar(kreativnost = 10, pouzdan = true, predznanje = 9, nadimak = "A") Student s1 = new Student("Student A", 1990, "FAKULTET 1", new Date()); @MojKomentar(kreativnost = 10, pouzdan = false, predznanje = 9, nadimak = "B") Student s2 = new Student("Student B", 1990, "FAKULTET 1", new Date()); @MojKomentar(kreativnost = 10, predznanje = 9, nadimak = "C")
Student s3 = new Student("Student C", 1990, "FAKULTET 1", new Date());
Anotacije počinu znakom „@“. U zagrade se navode imena atributa sa dodijeljenom vrijednošću. Atributi se
odvajaju zarezom ",". Ukoliko se u anotaciji ne koriste atributi zagrade se moraju izostaviti.
Detaljniji opis anotacija za korištenje JPA alata nije potreban.
2Java fajl (*.java) može da sadrži klase (class), interfejsa (interface), anotacije (@interface) i enumeracije (enum)
Java Persistence API sa fokusom na Hibernate v1.0 draft
16
3. JPA
Proces mapiranja Java klasa sa tabelama (i obrnuto) se naziva Object-relational mapping (ORM). Java Persistence
API (JPA) predstavlja jedan od pristupa ORM-a za programski jezik Java, kao što su to Entity Framework, Linq to
SQL, NHibernate, NPersist i dr. za programski jezik C#.
JPA omogućava slanje, izmjenu i preuzimanje podataka iz relacionih baza podataka bez potrebe korištenjaa SQL
izraza.
JPA predstavlja samo specifikaciju za pristup i dok za implementaciju se koriste posebne biblioteke (framework)
koji se obično nazivaju persistence provider (u daljnjem tekstu provajderi).
JPA se sastoji od klasa, interfesja i anotacija koje su grupisane u package javax.persistence, a mogu se koristiti na
JavaSE i JavaEE platformi.
Tri glavne komponente, koje JPA definira, su:
Anotacije i metapodaci za ORM
Entity Manager API - automatizuje slanje, izmjenu i preuzimanje podataka iz baze podataka
Java Persistence Query Language (JPQL) - jezik za izvršavanje upita sličan SQL-u.
Mapiranje Java klasa sa tabelama relacione baze podataka je definisano u posebnim metapodacima (Persistence
metadata). Za definisanje metapodataka koriste se Java anotacije u mapiranoj Java klasi.
Provajderi koristiti metapodatke kako mogao izvršiti ispravne operacije nad bazom podataka. Većina provajdera
ima mogućnost generisanja tabela u postojećoj bazi podataka na osnovu metapodataka.
3.1 Entity
Svaku klasu koju želimo sačuvati u bazi podataka potrebno je opisati anotacijom @Entity (iz paketa
javax.persistence.*). Tako opisana klasa se naziva Entity. JPA će za svaku entity-klasu kreirati tabelu u bazi
podataka. Instance entity-klasa će predstavljati zapise u tabeli.
import javax.persistence.Entity; @Entity public class Student {
...
Za svaku entity-klasu vrijedi slijedeće:
mora sadržavati defaultni konstruktor (ukoliko postoje drugi konstruktori sa formalnim parametrima)
ne smije biti final-klasa (final class je klasa koja se ne može naslijediti – sadrži ispred imena klase ključnu
riječ final)
mora sadržavati definisan primarni ključ
svaki član klase mora da ima getter i setter-funkcije. Ovakva klase se još naziva POJO class3
3POJO: Klasa koja se sastoji isključivo od članova i odgovarajućih gettera i setttera se naziva još i POJO klasa (Plain Old Java
Object). Ekvivalent POJO-klasa u.NET framework-u je Plain Old CLR Object.
Java Persistence API sa fokusom na Hibernate v1.0 draft
17
3.1.1 Imenovanje tabele
Ime tabele je defaultno isto kao ime klase. Ukoliko se želi koristiti drugačije ime tabele, potrebno je dodati
anotaciju @Table i njen atribut name.
import javax.persistence.Entity; @Entity @Table(name="TblStudent") public class Student {
...
3.2 Field
Field predstavlja atribut ili član klase. Članovi klase mogu da budu primitivni tipovi podataka (npr. int, float) ili kompleksni tipovi (npr. klasa Opcina)
3.2.1 Označavanje
Vrijednosti atributa entity-klase se smještaju u pripadajuće kolona u tabeli. Postoje dva načina označavanja
atributa klase:
direktno označavanje atributa
označavanje pripadajuće getter-funkcije
U jednoj klasi se ne smiju miješati spomenute metode označavanja. U koliko se označavaju getter-funkcije onda
one prema Java konvenciji moraju biti imenovane na slijedeći način: funkcija treba da počinje sa „get“ a potome
slijedi naziv atributa. Prvo slovo naziva atributa treba da bude veliko slovo (npr. getIme). Većina razvojnih
okruženja ili UML alata ima mogućnost generisanja getter i setter-funkcija.
Varijanta 1: direktno označanje člana klase
Varijanta 2: označanje pripadajuće getter-funkcije
@Entity public class Student { @Id private Integer idStudenta;
private String ime; private Integer godiste; private String fakultet;
public Integer getIdStudenta() { return id; } public void setIdStudenta(Integer id) { this.id = id; }
...
}
@Entity public class Student { private Integer idStudenta;
private String ime; private Integer godiste; private String fakultet;
@Id
public Integer getIdStudenta() { return id; } public void setIdStudenta(Integer id) { this.id = id; }
...
}
3.2.2 Primarni ključ
Primarni ključ može da bude kompozicija više članova (za koji se koristi posebne klase koje sadrže članove
primarnog ključa) ili samo jedan član, a označava se anotacijom @Id. (Pogledati primjere iznad).
Java Persistence API sa fokusom na Hibernate v1.0 draft
18
3.2.3 Generisanje vrijednosti primarnog ključa
Primarni ključ može biti automatski generisan ukoliko je označen anotacijom @GeneratedValue.
@Entity public class Student { @Id @GeneratedValue private Integer id; private String ime; private Integer godiste; private String fakultet; // ...
Ukoliko je potrebno definisat način generisanja vrijednosti može se koristiti atribut strategy sa mogućim
vrijednostima AUTO, IDENTITY, SEQUENCE, TABLE., npr. @GeneratedValue(strategy = GenerationType.AUTO)
3.2.4 Ignorisanje članova (Transient)
Po defaultu za svaki član će biti kreirana kolona, izuzev ako je označen anotacijom @Transient. Slijedi primjer
ignorisanja člana mojKomentar.
@Entity public class Student { @Id private Integer id; private String ime; private Integer godiste; private String opis; @Transient private String mojKomentar; //...
3.2.5 Imenovanje kolona
Nazivi kolona su defaultno isti kao nazivi atributa klase. Ukoliko se žele koristiti druga imena potrebno je koristiti
anotaciju @Column.
@Entity @Table(name = "tblStudent") public class Student { @Id private Integer id; private String ime; private Integer godiste; @Column(name = "student_opis") private String opis; //...
3.2.6 Dužina stringova
Za član String se koristi odgovarajući tip podatka koji pamti karaktere (npr. varchar). Defaultna dužina vrijednosti
u koloni jeste 255. Ova vrijednost se može izmijeniti pomoći atributa length u anotaciji @Column.
@Entity public class Student { @Id
Java Persistence API sa fokusom na Hibernate v1.0 draft
19
private Integer id; private String ime; private Integer godiste; private String fakultet; @Column(length = 2000) private String mojKomentar; private Date datum; // ...
3.2.7 Dozvola null-vrijednosti
Defaultno svaki član entity-klase može pamti null vrijednost, tj. njegova kolona može da primi vrijednost null. Ovo
se može izmijeniti atributom nullable u anotaciji @Column.
@Entity public class Student { @Id private Integer id; @Column(nullable = false) private String ime; @Column(nullable = false) private String prezime; //...
Ukoliko dozvolite null-vrijednosti onda ne smijete koristiti primitivne tipove podataka (int, long, float, double)
već morate koristiti pripadajuće wrapper-klase (Integer, Long, Float, Double). Primitivni tipovi ne mogu pamitit null
vrijednost, dok varijabla klase to može. Primitivni tipovi su u Java Editorima obično označeni tamno crvnom bojom.
3.2.8 Temporal
Za član tipa java.util.Date potrebno je odrediti koji je to tip podatka u bazi podataka (date, time i timestamp).
Ovaj tip se može odrediti anotacijom @Temporal. Ukoliko se ova anotacija izostavi neki JPA provajderi će prijaviti
grešku (npr. Eclipse Link provajder) a neki će koristiti DATE kao podrazumijevanu vrijednost (Hibernate provajder).
@Temporal(TemporalType.DATE) private Date datum;
@Temporal(TemporalType.TIME) private Date datum;
@Temporal(TemporalType.TIMESTAMP) private Date datum;
3.2.9 Lob
@Lob se koristi za mapiranje većih objekata, i to tekstualnih (CLOB – character large object) i binarnih (BLOB –
binary large object).
@Lob
private char[] pismo; @Lob
private String pismo;
@Lob
private byte[] slika;
@Lob
private Byte[] slika;
Preporučuje se kod većih objekata korištenje opcije Fetch-Lazy koja učitava podatke iz baze tek prilikom prvog
pristupa članu (ukoliko sesija nije prethodno zatvorena). Detaljnije o Lazy-učitavanju podataka možete pogledati u
podpoglavlju 3.5.7.1. Primjer je dat u nastavku.
Java Persistence API sa fokusom na Hibernate v1.0 draft
20
@Lob @Basic(fetch=FetchType.LAZY) private byte[] dokument;
3.2.10 Zadatak
Sada je vrijeme da pročitano isprobate. Kako biste mogli testirati potrebno je prvo instalirati jedan od JPA
provajdera. Pročitajte poglavlja 4.1 i 4.2, te instalirajte provajder Hibernate. Kreirajte jednu Entity klasu Student.
Preskočite poglavlja 3.3 i 3.4 te pređite na poglavlje 3.5 (sa fokusom na 3.5.5.1). Dodajte nekoliko zapisa u bazu
podataka. Nakon što uspješno dodate zapise u tabelu, ponovo ih učitajte iz baze (poglavlje 3.5.5.2).
Nakon uspješnog testiranja slanja objekata u bazu te uzimanja objekata iz baze, nastavite dalje sa čitanjem
podpoglavlja 3.3. (asocijacije).
3.3 Asocijacije
JPA omogućava definisane relacija između klasa. Postojeće tri grupe relacija (sa pripadajućim anotacijama):
@OneToOne
@ManyToOne i @OneToMany
@ManyToMany
Podsjećamo da se veze između dvije klase mogu posmatrati kao:
Unidirekcionalne
o Klasa A vidi klasu B (dok B ne vidi klasu A)
Bidirekcionalne
o Klasa A vidi klasu B, također, klasa B vidi klasu A
3.3.1 OneToOne
Na slici 3.1 je prikazana one-to-one asocijacija između klase A i klase B sa kardinalitetima 0..1 i 0..1.
asocijacija AB
0..1
roleB
0..1
roleA
A B
Slika 3.1. Asocijacija one-to-one između klase A i klase B (PowerDesigner)
Direktni pristup objekta jedne klase prema objektu druge klase se određuje opcijom Navigable (unutar
PowerDesignera u postavkama asocijacije). Inače, uspostavljena veza (asocijacija) se može posmatrati na sljedeća
tri načina:
[1] unidirekcionalna prema klasi B
[2] unidirekcionalna prema klasi A
[3] bidirekcionalna
Grafički prikaz one-to-one asocijacije na osnovu postavke opcije Navigable u alatu PowerDesigner kao i primjer
implementacije asocijacije u programskoj jeziku Java su prikazani u tabeli 3.1.
Java Persistence API sa fokusom na Hibernate v1.0 draft
21
Grafički prikaz u alatu PowerDesigner Primjer Implementacije klase A i klase B
1 Unidirekcionalna prema klasi B
asocijacija AB
0..1
roleB
0..1
roleA
A B
public class A { B roleB; ... }
public class B { ...
}
2 Unidirekcionalna prema klasi A
asocijacija AB
0..1
roleB
0..1
roleA
A B
public class A { ...
}
public class B { A roleA; ...
}
3 Bidirekcionalna
asocijacija AB
0..1
roleB
0..1
roleA
A B
public class A { B roleB; ...
}
public class B { A roleA; ...
}
Tabela 3.1. Grafički prikaz one-to-one asocijacija u alatu PowerDesigner
Što se tiče implementacije on-to-one veza između dva entiteta (A i B), ona je moguća na jedan od slijedeća dva
načina:
[1] entiteti A i B koriste istu vrijednost za primarni ključ
[2] primarni ključ prvog entiteta A će predstavljati strani ključ u drugom entitetu B
U nastavku su data objašnjena obe vrste implementacija.
3.3.1.1 Dijeljenje zajedničkog primarnog ključa
Kod ove vrste implementacije one-to-one veze ista vrijednost za primarni ključ se koristi kod oba entiteta. Ovdje je
potrebno voditi računa o načinu dodjeljivanja vrijednosti primarnom ključu. @GeneratedValue se ne može koristiti
u oba entiteta jer samo jedna jedan entitet može imati generisanu vrijednost primarnog ključa dok će ta ista
vrijednost biti dodijeljena drugom entitetu
U nastavku su dati primjer definisana one-to-one veze između klase Student i KorisnickiNalog metodom dijeljenja zajedničkog primarnog ključa. Jedna od klasa mora biti odabrana kao nosilac veze. Odabrana je klasa Student.
Java Persistence API sa fokusom na Hibernate v1.0 draft
22
Dijeljenje zajedničkog primarnog ključa (Unidirekcionalni veza)
@Entity public class Student { @Id @GeneratedValue private Integer id; private String ime; private String prezime; @OneToOne(cascade = CascadeType.ALL) @PrimaryKeyJoinColumn private KorisnickiNalog korisnickiNalog; ...
@Entity public class KorisnickiNalog { @Id @GeneratedValue private Integer id; private String username; private String password; ...
Dijeljenje zajedničkog primarnog ključa (Bidirekcionalni veza)
@Entity public class Student { @Id @GeneratedValue private Integer id; private String ime; private String prezime; @OneToOne(cascade = CascadeType.ALL) @PrimaryKeyJoinColumn private KorisnickiNalog korisnickiNalog; ...
@Entity public class KorisnickiNalog { @Id @GeneratedValue private Integer id; private String username; private String password; @OneToOne(mappedBy = "korisnickiNalog") private Student student; ...
Klasa KorisnickiNalog može da ima referencu na klasu Student (tj. da "vidi" ili "poznaje" studenta). U tom
slučaju (kada A vidi B, te B vidi A) može se reći da između A i B postoji bidirekcionalna veza (dvosmjerna). Tada se
na atribut student (u klasi KorisnickiNalog) dodaje se anotacija @OneToOne sa atributom mappedBy koji označava
član u klasi Student koji referencira na klasu KorisnickiNalog.
U implementacijima unidirekcionalne i bidirekcionalne veze nema razlike u definisanju klasa koja je nosilac veze (u ovom slučaju klasa Student).
U prethodnim primjerima, kao i u narednim primjerima, je uključena opcija CascadeType.ALL koja omogućava da
se automatski šalje novi objekat klase KorisnickiNalog prilikom slanja objekta klase Student u bazu (pogledati
podpoglavlje 3.5.6.3).
Naziv člana u klasi
Student koji referencira
na ovu klasu.
Nosilac veze se razliku po tome što ne koristi
atribut mappedBy u @OneToOne.
Java Persistence API sa fokusom na Hibernate v1.0 draft
23
3.3.1.2 Korištenje vrijednost primarnog ključa kao strani ključ u drugom entitetu
U nastavku su dati primjer definisana one-to-one veze korištenjem vrijednost primarnog ključa jednog entiteta
(npr. KorisnickiNalog) kao strani ključ u drugom entitetu (npr. Student). Ovdje je odabrana je klasa Student
kao nosilac veze.
Korištenje vrijednost primarnog ključa kao strani ključ u drugom entitetu (Unidirekcionalni veza)
@Entity public class Student { @Id @GeneratedValue private Integer id; private String ime; private String prezime; @OneToOne(cascade = CascadeType.ALL) @PrimaryKeyJoinColumn private KorisnickiNalog korisnickiNalog; ...
@Entity public class KorisnickiNalog { @Id @GeneratedValue private Integer id; private String username; private String password; ...
Korištenje vrijednost primarnog ključa kao strani ključ u drugom entitetu (Bidirekcionalni veza)
@Entity public class Student { @Id @GeneratedValue private Integer id; private String ime; private String prezime; @OneToOne(cascade = CascadeType.ALL) @JoinColumn private KorisnickiNalog korisnickiNalog; ...
@Entity public class KorisnickiNalog { @Id @GeneratedValue private Integer id; private String username; private String password; @OneToOne(mappedBy = "korisnickiNalog") private Student student; ...
Anotacija @JoinColumn dodaje novu kolonu sa defaultnim imenom "Tabela_id". Ime kolone se može izmijeniti
atributom name, tj. @JoinColumn(name="imeKolone").
Naziv člana u klasi
Student koji referencira
na ovu klasu.
Nosilac veze se razliku po tome što ne koristi
atribut mappedBy u @OneToOne.
Kreira se nova
kolona.
Defaultno ime je
korisnickiNalog_id
Java Persistence API sa fokusom na Hibernate v1.0 draft
24
3.3.2 ManyToOne i OneToMany
Na slici 3.2 je prikazana veza asocijacije između klase A i klase B sa kardinalitetima 0..1 i 0..*.
asocijacija AB
0..1
roleA
0..*
roleB
A B
Slika 3.2. Asocijacija između klase A i klase B (PowerDesigner)
Direktni pristup objekta jedne klase prema objektu druge klase se određuje opcijom Navigable (unutar
PowerDesignera u postavkama asocijacije). Inače, uspostavljena veza (asocijacija) se može posmatrati na sljedeća
tri načina:
[4] unidirekcionalna prema klasi B
[5] unidirekcionalna prema klasi A
[6] bidirekcionalna
Grafički prikaz asocijacije na osnovu postavke opcije Navigable u alatu PowerDesigner kao i primjer implementacije
asocijacije u programskoj jeziku Java su prikazani u tabeli 3.2.
Grafički prikaz u alatu PowerDesigner Primjer Implementacije klase A i klase B
1 Unidirekcionalna prema klasi B
asocijacija AB
0..1
roleA
0..*
roleB
A B
ovo su defaultne postavke u power designeru, nisu preporučljive za JPA
public class A { List<B> roleB; ... }
public class B { ...
}
2 Unidirekcionalna prema klasi A
asocijacija AB
0..1
roleA
0..*
roleB
A B
public class A { ...
}
public class B { A roleA; ...
}
3 Bidirekcionalna
asocijacija AB
0..1
roleA
0..*
roleB
A B
public class A { List<B> roleB; ...
}
public class B { A roleA; ...
}
Tabela 3.2. Grafički prikaz many-to-one i one-to-many asocijacija u alatu PowerDesigner
ManyToOne - veza
ManyToOne - veza OneToMany - veza
OneToMany - veza
Java Persistence API sa fokusom na Hibernate v1.0 draft
25
3.3.2.1 Unidirekcionalna veza
Primjer 1 iz tabele 3.2 predstavlja implementacija veze od klase „jedan“ prema klasi „više“ (veza poznata pod
nazivom one-to-many) i to bez implementacije veze od klase „više“ prema klasi „jedan“ (many-to-one). Ovakva
unidirekcionalna veza u JPA nije od koristi s obzirom da je vidljivost u relacionim bazama, ipak, suprotna. U bazama
podataka su dvije tabele povezane preko stranih ključeva u kojem zapis iz tabele „više“ poznaje zapis iz tabele
„jedan“ (many-to-one) a ne obrnuto. Što više, defaultne postavke novokreirane asocijacije između dvije klase u
alatu PowerDesigner su upravo ovakve (unidirekcionalne sa jednom one-to-many vezom – primjer 1). Stoga, ako se
koristi alat PowerDesigner za generisanje Java code iz dijagrama klasa (kojeg ćete koristiti za JPA anotacije)
potrebno je okrenuti sve veze u suprotnu stranu (tako da postanu unidirekcionalne many-to-one veze, primjer 2) ili
ih postaviti kao bidirekcionalne (primjer 3).
Implementacija unidirekcionalne veze između klase Student i klase Opcina sa anotacijom @ManyToOne je data u
nastavku.
Unidirekcionalna many-to-one veza
@Entity public class Student { @Id @GeneratedValue private Integer id; private String ime; private String prezime;
@ManyToOne private Opcina opcina; ...
@Entity public class Opcina { @Id @GeneratedValue private Integer id;
private String naziv; ...
ukoliko potrebno promjeniti ime kolone dodajte anotaciju
@JoinColumn(name="ime_FK_kolone")
ukoliko potrebno postaviti član kao obavezno polje dodajte atribut
@ManyToOne(optional = false)
Java Persistence API sa fokusom na Hibernate v1.0 draft
26
3.3.2.2 Bidirekcionalna veza
Primjer 3 iz tabele 3.2 predstavlja implementacija veze od klase „jedan“ prema klasi „više“ (one-to-many) sa implementacijom veze od klase „više“ prema klasi „jedan“ (many-to-one). Bidirekcionalna veza se može koristiti za implementiranje relacija tipa kompozicija gdje klasa „više“ (npr. NastavnaJedinca) ne može postojati bez klase „jedan“ (npr. Predmet) i u kome klasa „jedan“ (npr. Predmet) se brine o objektima klase „više“ (npr. NastavnaJedinca).
Implementacija bidirekcionalne veze između klase NastavnaJedinca i klase Predmet sa anotacijama @ManyToOne
i @OneToMany je data u nastavku.
Bidirekcionalna veza (sa ManyToOne + OneToMany anotacijama)
@Entity public class NastavnaJedinca { @Id @GeneratedValue private Integer id;
private String naziv;
@ManyToOne private Predmet predmet; ...
@Entity public class Predmet { @Id @GeneratedValue private Integer id;
private String naziv;
@OneToMany(mappedBy = "predmet", cascade = CascadeType.ALL) private List<NastavnaJedinca> nastavneJedinice; //java.util.List ...
Kolekcije (kao npr. podataka tipa List) je potrebno alocirati. Ukoliko koristiti Power Designer, onda će bit
generisan Java code koji će prilikom prvog pristupa alocirati kolekciju kao u nastavku:
@Entity public class Predmet { @Id @GeneratedValue private Integer id;
private String naziv;
@OneToMany(mappedBy = "predmet", cascade = CascadeType.ALL) private List<NastavnaJedinca> nastavneJedinice; public List<NastavnaJedinca> getNastavneJedinice() { if (nastavneJedinice == null) nastavneJedinice = new ArrayList<NastavnaJedinca>(); return nastavneJedinice; }
Kolekcije se, također, mogu i ručno alocirati prilikom definisanja varijable:
@Entity public class Student { @Id @GeneratedValue private Integer id; private String ime; private String prezime; private List<Predmet> omiljeniPredmeti = new ArrayList<Predmet>();; //getter i setter public List<Predmet> getOmiljeniPredmeti() { return omiljeniPredmeti; }
Naziv člana u klasi
NastavnaJedinca koji
referencira na klasu
Predmet.
Java Persistence API sa fokusom na Hibernate v1.0 draft
27
3.3.3 ManyToMany
Many-to-many veza između klasa A i B se može implementirati na dva načina:
1. dodavanjem nove (treće) entity-klase X te uz korištenje dvije many-to-one asocijacije (A -> X, te B -> X);
kod ovog načina možete dodavati atribute veze (atributi u klasi X)
2. korištenjem anotacije @ManyToMany (automatski se kreira nova tabela)
Veza implementirana anotacijom @ManyToMany može biti unidirekcionalna i bidirekcionalna. U nastavku je dat
primjer many-to-many veze između klase Student i klase Predmet sa ulogom omiljeniPredmeti.
Unidirekcionalna many-to-many veza
@Entity public class Student { @Id @GeneratedValue private Integer id; private String ime; private String prezime;
@ManyToMany private List<Predmet> omiljeniPredmeti; ...
@Entity public class Predmet { @Id @GeneratedValue private Integer id; private String naziv; ...
Bidirekcionalni many-to-many veza
@Entity public class Student { @Id @GeneratedValue private Integer id; private String ime; private String prezime;
@ManyToMany private List<Predmet> omiljeniPredmeti; ...
@Entity public class Predmet { @Id @GeneratedValue private Integer id; private String naziv;
@ManyToMany(mappedBy = "omiljeniPredmeti") private List<Student> studenti; //java.util.List ...
U implementaciji bidirekcionalne many-to-many veze klasa Student je odabrana kao nosilac veze. (Nosilac veze se
razlikuje po tome što ne sadrži atribut mappedBy u anotaciji @ManyToMany).
Naziv člana u klasi
Student koji referencira
na klasu Predmet.
Kreira se dodatna tabela.
Koristit će se defaultno ime: <Tabela1>_<Tabela2>
Ukoliko potrebno promjeniti ime dodajte anotaciju
@JoinTable(name = "Ime_nove_tabele")
Java Persistence API sa fokusom na Hibernate v1.0 draft
28
3.4 Nasljeđivanje
Objektno orijentisano nasljeđivanje klasa se može preslikati u racionalnu bazu podataka na slijedeća tri načina:
1. Single table mapping
2. Joined-tables mapping
3. Tabel-per-class mapping
JPA provajder su obavezni implementirati prva dva načina, s toga će samo oni biti opisani u nastavku.
3.4.1 Single table mapping
Kod ove metode koristi se samo jedna tabela za baznu i proširenu klasu. Kako bi se zapisi mogli razlikovati kojoj
klasi pripadaju, kreira se dodatna kolona (diskriminator kolona), čije je defaultno ime DTYPE tipa varchar(31).
Prednost:
Što se tiče performansi ovo je najefikasnija metoda nasljeđivanja jer sve izmjene u bazi izvršavaju u jednoj
tabeli.
Nedostatak:
S obzirom da se tabela sastoji od kolona koji pripadaju različitim klasama, onda će kolone proširene klase
u zapisima, koji pripadaju samo baznoj klasi, uvijek biti prazne (null-vrijednost).
Primjer
Koristit ćemo baznu klasu Student i proširenu klasu Demonstrator.
Bazna klasa Proširena klasa
@Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) public class Student { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; private String ime; private String prezime; ...
@Entity public class Demonstrator extends Student { private String napomena; ...
Generisat će se tabela Student koja će sadržavati zajedničke kolone:
Student
DTYPE varchar(31)
id int
ime varchar(255)
prezime varchar(255)
napomena varchar(255)
Column Name Data Type Allow Nulls Identity
Slika 3.3. Generisana tabela kod nasljeđivanja (Single table mapping)
Java Persistence API sa fokusom na Hibernate v1.0 draft
29
3.4.2 Joined-tables mapping
Kod ove metode koristi se za svaku klasu zasebna tabela. Zapisi iz različitih tabela se prepoznaju kao jedna cjelina
tako što se koristit zajednička vrijednosti za primarne ključeve.
Prednost:
Baza podataka je normalizovana.
Jednostavnija modifikacija strukture klase i pripadajućih tabela.
Nedostatak:
Što se tiče performansi ovo je najmanje efikasna metoda nasljeđivanja pogotovo ako pristup bazi se vrši
na proširenijim klasama. Npr. za situaciju „C naslijedilo B, te B naslijedilo A“ potrebno je uvijek pristupati u
dvije ili tri tabele kako se raspoznao tip podataka (klasa tipa A, B ili C).
Primjer
Koristit ćemo baznu klasu Student i proširenu klasu Demonstrator.
Bazna klasa Proširena klasa
@Entity @Inheritance(strategy = InheritanceType.JOINED) public class Student { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; private String ime; private String prezime; ...
@Entity public class Demonstrator extends Student { private String napomena; ...
Tabela Demonstrator će sadržavati kolonu ID čije će vrijednosti biti jednake vrijednostima bazne klase.
Student
id int
ime varchar(255)
prezime varchar(255)
Column Name Data Type Allow Nulls IdentityDemonstrator
napomena varchar(255)
id int
Column Name Data Type Allow Nulls Identity
Slika 3.4. Generisana tabela kod nasljeđivanja (Joined-tables mapping)
3.5 Entity Manager
Entity Manager predstavlja posebnu klasu koja nudi operacije pristupa bazi podataka, kao npr.
slanje objekata u bazu podataka (za definisane entity-klase)
primanje objekata iz baze podataka na osnovu ID-a (za definisane entity-klase)
izvršavanje upita
otvaranje i zatvaranja sesije (konekcije)
upravljanje transakcijama
Java Persistence API sa fokusom na Hibernate v1.0 draft
30
3.5.1 JPA postavke
Nakon što definisanja domain sloj aplikacije kroz implementacije entity-klasa potrebno je definisati globalne JPA
postavke. Ove postavke se zapisuju u fajl persistence.xml unutar foldera META-INF.
U narednom primjeru prikazan je persistence.xml sa postavkama koje su neovisne o JPA provajderu.
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="MojaOznaka1" transaction-type="RESOURCE_LOCAL"> <exclude-unlisted-classes>false</exclude-unlisted-classes> <properties> <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost/imeBaze" /> <property name="javax.persistence.jdbc.user" value="root" /> <property name="javax.persistence.jdbc.password" value="nekaLozinka" /> <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" /> </properties> </persistence-unit> </persistence>
U gornjem primjeru su definisane slijedeće postavke:
1. Persistence-unit oznaka
Potrebno je dodijeliti oznaku za svaki persistence unit (skup postavki). U jednom fajlu je moguće definisati
više postavki (npr. postavke za server 1, server 2, itd). Naziv, koji se ovdje definira, potrebno je navesti
prilikom instanciranja klase EntityManagerFactory.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("MojaOznaka1");
2. JDBC Connection URL
3. JDBC Username
4. JDBC Password
5. JDBC Driver class4
3.5.2 EntityManagerFactory
Objekti klase EntityManager predstavljaju ostvarene konekcije na bazu podataka. Objekti EntityManager se
kreiraju pomoću klase EntityManagerFactory koja koristi JPA definisane postavke. Postavke se učitavaju iz fajla
persistence.xml prilikom kreiranja objekta EntityManagerFactory. Preporučuje se korištenje samo jedne instance
klase EntityManagerFactory u čitavom programu.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("MojaOznaka1");
Pozivom funkcije close zatvara se baza (ako je riječ o bazi kojoj se pristupa preko fajla, npr. Apache Derby, SqlLite,
JavaDB).
emf.close();
4Driver class - Kao što je već prethodno napisano nije potrebno navoditi klasu koja implementira Type 4 drajvere, te se tag može
izbrisati. Međutim, neki će JPA provajder će u tom slučaju prikazati poruku upozorenja iako će raditi potpuno ispravno.
Java Persistence API sa fokusom na Hibernate v1.0 draft
31
3.5.3 Instanciranje EntityManager-a
Instanciranje klase EntityManager i otvaranje konekcije se vrši funkcijom createEntityManager.
EntityManager em = emf.createEntityManager(); // pristup bazi em.close();
Kada konekcija više nije potrebna, neprohodno je zatvoriti konekciju funkcijom close nakon čega se oslobađaju
zauzeti resursi (pointer na fajl, socket na udaljeni server i sl.). Takav objekat EntityManager se ne može više
koristiti.
3.5.4 EntityTransacion
Operacije koje mijenjaju sadržaj baze (izmjena, dodavanje i brisanje) je potrebno izvršavati unutar aktivne
transakcije. Transakcija se započinje pozivom funkcijom begin unutar objekta klase Transaction.
em.getTransaction().begin(); // update, insert, delete em.getTransaction().commit();
Modifikacije na aktivnoj transakciji se pamte u memoriji. Pozivom funkcije commit izmjene se fizički primjenjuju u
bazi podataka kroz izvršavanjem generisanih SQL izraza.
3.5.5 Rollback
Ukoliko se desi greška prilikom modifikacije baze unutar aktivne transakcije te funkcija commit ne bude pozvana,
onda je moguće modifikacije poništiti pozivom funkcije rollback.
try { em.getTransaction().begin(); // update, insert, delete em.getTransaction().commit(); } catch (Exception e) { em.getTransaction().rollback(); }
Java Persistence API sa fokusom na Hibernate v1.0 draft
32
3.5.6 Dodavanje objekata u bazu podataka
Životni vijek entity objekata pri njihovom dodavanju u bazu podataka je prikazan u narednim primjerima.
DB
s: Manageds: New/
Transients: Detached
Student s =
new Student()
em.clear() /
em.close()em.persist(s)
em.getTransaction().commit() /
em.flush()
"INSERT INTO Student...VALUES..."
Slika 3.5. Životni vijek objekta pri dodavanju u bazu
3.5.6.1 Primjer dodavanja objekta bez upotrebe asocijacija
EntityManager em = emf.createEntityManager(); Student s1 = new Student(); s1.setIme("neko ime 1"); Student s2 = new Student(); s2.setIme("neko ime 2"); em.getTransaction().begin(); em.persist(s1); em.persist(s2); em.getTransaction().commit(); // 2x INSERT INTO Student... em.close(); //zatvara se konekcija
3.5.6.2 Primjer dodavanja objekta sa stranim ključem uz korištenje veze ManyToOne
public class Student { ... @ManyToOne private Opcina opcina; ... EntityManager em = emf.createEntityManager(); Student s1 = new Student(); s1.setIme("neko ime 1"); Opcina op = em.find(Opcina.class, 1); // SELECT * FROM Opcina WHERE ID = 1 s1.setOpcina(op); // Studentu se dodjeljuje objekat opcina kao strani ključ em.getTransaction().begin(); em.persist(s1); em.getTransaction().commit(); // INSERT INTO Student...
em.close(); // zatvara se konekcija
Java Persistence API sa fokusom na Hibernate v1.0 draft
33
3.5.6.3 Primjer dodavanja objekta uz korištenje veze OneToMany (Kompozicija)
@Entity public class NastavnaJedinca { @Id @GeneratedValue private Integer id;
private String naziv;
@ManyToOne private Predmet predmet; ...
@Entity public class Predmet { @Id @GeneratedValue private Integer id;
private String naziv;
@OneToMany(mappedBy = "predmet", cascade = CascadeType.ALL) private List<NastavnaJedinca> nastavneJedinice= new ArrayList<>(); ...
EntityManager em = emf.createEntityManager(); Predmet pr = new Predmet(); pr.setNaziv("Matematika"); NastavnaJedinica n1 = new NastavnaJedinica(); n1.setNaziv("Lekcija 1"); pr.getNastavneJedinice().add(n1); NastavnaJedinica n2 = new NastavnaJedinica(); n2.setNaziv("Lekcija 2"); pr.getNastavneJedinice().add(n2); NastavnaJedinica n3 = new NastavnaJedinica(); n3.setNaziv("Lekcija 3"); pr.getNastavneJedinice().add(n3); em.getTransaction().begin(); em.persist(pr); em.getTransaction().commit(); // 1x INSERT INTO Predmet... 3x INSERT INTO NastavnaJedinica...
em.close(); // zatvara se konekcija
3.5.6.4 Primjer dodavanja objekta sa zapisima u međutabelu uz korištenje veze ManyToMany.
public class Student { ... @ManyToMany @JoinTable(name = "Student_OmiljeniPredmeti") private List<Predmet> omiljeniPredmeti = new ArrayList<>();
...
EntityManager em = emf.createEntityManager(); Student s1 = new Student(); s1.setIme("neko ime 1"); Predmet pr1 = em.find(Predmet.class, 1); // SELECT * FROM Predmet WHERE ID = 1 s1.getOmiljeniPredmeti().add(pr1); Predmet pr2 = em.find(Predmet.class, 2); // SELECT * FROM Predmet WHERE ID = 2 s1.getOmiljeniPredmeti().add(pr2); Predmet pr3 = em.find(Predmet.class, 3); // SELECT * FROM Predmet WHERE ID = 3 s1.getOmiljeniPredmeti().add(pr3); em.getTransaction().begin(); em.persist(s1); em.getTransaction().commit(); // 1x INSERT INTO Student... 3x INSERT INTO Student_OmiljeniPredmeti...
em.close(); // zatvara se konekcija
Java Persistence API sa fokusom na Hibernate v1.0 draft
34
3.5.7 Uzimanje objekata iz baze podataka
Primjer jednostavnog učitavanja objekata iz baze je dat u nastavku.
DB
s: Managed s: Detached
em.clear() /
em.close()
Student s = em.find...
Student s = em.createQuery...
"SELECT FROM Student WHERE..."
Slika 3.6. Životni vijek objekta pri učitavanju iz baze
Pristup objektu i njegovim članovima je moguć prije i poslije zatvaranja konekcije. Primjer pristupa prije zatvaranja
konekcije je dat u nastavku.
EntityManager em = emf.createEntityManager(); Student s = em.find(Student.class, 1); //SELECT FROM Student WHERE id = 1 System.out.println("Ime: " + s.getIme()); System.out.println("Prezime: " + s.getPrezime()); em.close(); // zatvaranje konekcije, objekat "s" postaje "Deatached"
Primjer pristup objektu i njegovim članovima poslije zatvaranja konekcije je prikazan u nastavku.
EntityManager em = emf.createEntityManager(); Student s = em.find(Student.class, 1); //SELECT FROM Student WHERE id = 1 em.close(); // zatvaranje konekcije, objekat "s" postaje "Deatached" System.out.println("Ime: " + s.getIme()); System.out.println("Prezime: " + s.getPrezime());
Pročitajte napomenu za inicijalizranje članova koja slijedi u nastavku.
3.5.7.1 (Ne) inicijaliziranje članova klase
Pristup nekom članu klase poslije zatvaranja konekcije je moguć samo ako je njega vrijednost prethodno
inicijalizirana (prilikom njegovom povlačenja iz baze podataka). Samo članovi koji su definisano kao EAGER će biti
implicitno inicijalizirani prilikom inicijaliziranja objekta entity-klase (objekat „s“ tipa Student, primjer iznad).
Svaki član entity-klase ima definisan FetchType, a on može da ima jednu od slijedeće dvije vrijednosti:
Java Persistence API sa fokusom na Hibernate v1.0 draft
35
[1] EAGER (default vrijednost za obične članove – oni članovi koji nisu kolekcije)
Prilikom uzimanja objekta iz baze podataka učitavaju i inicijaliziraju se svi članovi koji su definsani kao
EAGER.
[2] LAZY (default vrijednost za kolekcije odnosno liste)
Prilikom uzimanja objekta iz baze podataka ne povlače i ne inicijaliziraju se vrijednosti koji su definisani
kao LAZY. Njihova vrijednost se može naknadno automatski inicijalizirati prilikom prvog pristupa članu i to
samo ukoliko se objekat entity-klase nalazi u stanju Managed (ne zatvaranje konekcije je preduslov), dok
će se u slučaju stanja Detached javit poruka o grešci. Ovakav način naknadnog inicijalizirana kada je to
potrebno je pogodno za članove čije vrijednosti zauzimaju više memorije (npr. dokumenti ili slike,
pogledati poglavlje 4.3.9.) ili za kolekcije kod bidirekcionalnih veza (pogledati poglavlje 4.4.3., primjer 2,
klasa Student).
Primjer defaultnih vrijednost (EAGER ili LAZY) unutar klase Student je dat u nastavku. @Entity public class Student { @Id @GeneratedValue private Integer id; FetchType (defaultna vrijednost) : EAGER
private String ime; FetchType (defaultna vrijednost) : EAGER
private String prezime; FetchType (defaultna vrijednost) : EAGER
@ManyToMany @JoinTable private List<Predmet> omiljeniPredmeti; FetchType (defaultna vrijednost) : LAZY
...
Promjena vrijednost „LAZY“ u „EAGER“ za kolekcije u many-to-many vezama se može izvršiti na jedan od dva
načina (pomoću atributa fetch u anotaciji @ManyToMany ili pomoću atributa fetch u anotaciji @Basic).
@Entity public class Student { ... @ManyToMany(fetch = FetchType.EAGER) @JoinTable private List<Predmet> omiljeniPredmeti; ...
@Entity public class Student { ... @Basic(fetch = FetchType.EAGER) @ManyToMany @JoinTable private List<Predmet> omiljeniPredmeti; ...
Promjena vrijednost „LAZY“ u „EAGER“ za kolekcije u one-to-many vezama se može izvršiti pomoću atributa fetch
u anotaciji @OneToMany ili @Basic.
@Entity public class Opcina { ... @OneToMany(fetch = FetchType.EAGER) private List<Student> studenti; ...
@Entity public class Opcina { ... @Basic(fetch = FetchType.EAGER @OneToMany private List<Student> studenti; ...
Promjena vrijednost „EAGER“ u „LAZY“ za obične članove (koji nisu kolekcije) se može izvršiti pomoću atributa
fetch u anotaciji @Basic ili @ManyToOne.
Java Persistence API sa fokusom na Hibernate v1.0 draft
36
@Entity public class Opcina { ... @ManyToOne(fetch = FetchType.LAZY) private Opcina opcina; ...
@Entity public class Opcina { ... @Basic(fetch = FetchType.LAZY) @ManyToOne private Opcina opcina; ...
@Entity public class Student { ... @Lob @Basic(fetch=FetchType.LAZY) private byte[] slika; ...
Ako su članovi u entity-klasi definisani kao LAZY, njihova vrijednost se ipak u JPQL upitima može povući i
inicijalizirati (kao da su EAGER) dodavanjem ključne riječi JOIN FETCH (pogledati poglavlje 3.6). Slijedi primjer.
SELECT s FROM Student s JOIN FETCH s.omiljeniPredmeti
Pristup članovima poslije zatvaranja konekcije je moguć samo za inicijalizirane vrijednosti.
EntityManager em = emf.createEntityManager(); Student s = em.find(Student.class, 1); //SELECT FROM Student WHERE id = 1 em.close(); // zatvaranje konekcije, objekat "s" postaje "Deatached" System.out.println("Ime: " + s.getIme()); System.out.println("Prezime: " + s.getPrezime()); List<Predmet> predmeti = s.getOmiljeniPredmeti(); for (Predmet p : predmeti) { System.out.println(p.getNaziv()); }
Pristup objektu prije zatvaranja konekcije.
EntityManager em = emf.createEntityManager(); Student s = em.find(Student.class, 1); //izvršava se: SELECT FROM Student WHERE id = 1 System.out.println("Ime: " + s.getIme()); System.out.println("Prezime: " + s.getPrezime()); List<Predmet> predmeti = s.getOmiljeniPredmeti(); // SELECT FROM Predmet WHERE ... for (Predmet p : predmeti) { System.out.println(p.getNaziv()); } em.close(); // zatvaranje konekcije
Bidirekcionalne veze:
Ako je veza definisana kao LAZY (defaultna
vrijednost) i ako je objekat u stanju
Detached, tj. ako je sesija (entity manager)
zatvorena, ovdje će se javiti greška.
Bidirekcionalne veze:
Ako je veza definisana kao LAZY (defaultna vrijednost) i ako je
objekat u stanju Managed, tj. ako sesija (entity manager) nije
zatvorena, onda će se select-upit iz tabele Predmet izvršiti tek
u ovoj liniji koda.
Java Persistence API sa fokusom na Hibernate v1.0 draft
37
3.5.8 Modifikacija objekata u bazi podataka
Modifikacija entity-objekata u bazi podataka se može vršiti na dva načina, i to:
držanjem sesije otvorenom (modifikacija objekata u stanju Managed)
zatvaranjem sesije (modifikacija objekata u stanju Detached)
3.5.8.1 Modifikacija objekata u stanju “Managed”
DB
s: Managed s: Detached
em.clear() /
em.close()
Student s = em.find...
Student s = em.createQuery...
"SELECT FROM Student WHERE..."
em.getTransaction().commit() /
em.flush()
"UPDATE Student SET... WHERE..."
s.setIme("...")
Slika 3.7. Životni vijek objekta pri modifikaciji u stanju „Managed“
// učitavanje objekta, npr. ovaj dio koda se može dodati u konstruktor windows forme EntityManager em = emf.createEntityManager(); Student s = em.find(Student.class, 1); //SELECT FROM Student WHERE id = 1 // nakon dužeg vremena, npr. korisnik klikne button za snimimanje: s.setIme(txtIme.getText()) s.setIme("novo ime"); em.getTransaction().begin(); em.getTransaction().commit(); //UPDATE Student SET ime = 'novo ime' WHERE id = 1 em.close();
Java Persistence API sa fokusom na Hibernate v1.0 draft
38
3.5.8.2 Modifikacija objekata u stanju “Detached”
DB
s: Managed s: Detached
em.clear() /
em.close()
Student s = em.find...
Student s = em.createQuery...
"SELECT FROM Student WHERE..."
em.getTransaction().commit() /
em.flush()
"UPDATE Student SET... WHERE..."
s.setIme("...")
s: Managed
em.merge(s)
s: Detached
em.clear() /
em.close()
nakon dužeg vremena
(npr. korisnik klikne na button)
Slika 3.8. Životni vijek objekta pri modifikaciji u stanju „Detached“
// učitavanje objekta, npr. ovaj dio koda se može dodati u konstruktor windows forme EntityManager em1 = emf.createEntityManager(); Student s = em1.find(Student.class, 1); em1.close(); // nakon dužeg vremena, npr. korisnik klikne button za snimimanje: s.setIme(txtIme.getText())
s.setIme("novo ime");
EntityManager em2 = emf.createEntityManager(); em2.getTransaction().begin(); em2.merge(s); em2.getTransaction().commit(); em2.close();
3.5.9 Brisanje objekata iz baze podataka
Brisanje entity-objekata u bazi podataka se može vršiti na dva načina, i to:
držanjem sesije otvorenom (brisanje objekata u stanju Managed)
zatvaranjem sesije (brisanje objekata u stanju Detached)
Java Persistence API sa fokusom na Hibernate v1.0 draft
39
3.5.9.1 Brisanje objekata u stanju “Managed”
DB
s: Managed s: Removed
em.remove(s)
Student s = em.find...
Student s = em.createQuery...
"SELECT FROM Student WHERE..."
em.getTransaction().commit() /
em.flush()
"DELETE FROM Student WHERE..."
s: Deatached
em.clear() /
em.close()
Slika 3.9. Životni vijek objekta pri brisanju u stanju „Managed“
EntityManager em = emf.createEntityManager(); Student s = em.find(Student.class, 1); //SELECT FROM Student WHERE id = 1 em.getTransaction().begin(); em.remove(s); em.getTransaction().commit(); //DELETE FROM Student WHERE id = 1 em.close();
3.5.9.2 Brisanje objekata u stanju “Detached”
Objekat u stanju Detached nije moguće direktno prebaciti u stanje Removed. Potrebno je ga je prethodno dovesti
u stanje Managed.
DB
s: Managed s: Detached
em.clear() /
em.close()
Student s = em.find...
Student s = em.createQuery...
"SELECT FROM Student WHERE..."
em.getTransaction().commit() /
em.flush()
"UPDATE Student SET... WHERE..."
s: Managed
em.merge(s)
s: Removed
em.clear() /
em.close()
s: Detached
em.remove(m)
nakon dužeg vremena
(npr. korisnik klikne na button)
Slika 3.10. Životni vijek objekta pri brisanju u stanju „Detached“
Java Persistence API sa fokusom na Hibernate v1.0 draft
40
EntityManager em1 = emf.createEntityManager(); Student s = em1.find(Student.class, 1); em1.close(); // ... nakon dužeg vremena, npr. korisnik klikne button za snimimanje EntityManager em2 = emf.createEntityManager(); em2.getTransaction().begin(); Student m = em2.merge(s); em2.remove(m); em2.getTransaction().commit(); em2.close();
3.5.10 Moguće promjene stanja entity-objekata
Pregled prelazaka između različitih stanja entity-objekata je prikazan u nastavku.
Slika 3.11. Moguće promjene stanja entity-objekata
5
3.6 JPQL – Java Persistence Query Language
JPA Query Language (JPQL) se može posmatrati kao objektno orijentisana varijanta SQL-a. Poznavaoci SQL-a mogu se veoma brzo prilagoditi JPQL-u. Primjer jednog JPQL upita.
SELECT s FROM Student AS s
Ključna riječ "as" se može kao i SQL-u izostaviti.
SELECT s FROM Student s
U prethodnom upitu za entitet "Student" korišten je alias "s". U SELECT klauzuli je potrebno navesti alias i nije
moguće koristiti "*" kao u SQL-u. Rezultat ovog upita jeste lista svih studenata "List<Student>" iz tabele Student.
5Preuzeto sa http://docs.oracle.com/html/E24396_01/ejb3_overview_em_lifecycle.html
Java Persistence API sa fokusom na Hibernate v1.0 draft
41
Za razliku od SQL-a gdje se navode imena tabela i kolona u JPQL-u se navode imena klasa i atributa članova (iako su najčešće ista imena). To znači, da su imena klasa i atributa CASE SENSETIVE.
SELECT s FROM Student s //ispravno: postoji klasa "Student"
SELECT s FROM STUDENT s //greška: ne postoji klasa "STUDENT"
Ključne riječi JPQL-a nisu ovisne o malim i velikim slovima.
SELECT s FROM Student s
select s from Student s
Naredni primjeri su preuzeti sa
http://openjpa.apache.org/builds/1.0.1/apache-openjpa-1.0.1/docs/manual/jpa_overview_query.html
Select statement se sastoji od slijedećih klauzula:
SELECT_CLAUSE FROM_CLAUSE [WHERE_CLAUSE] [GROUPBY_CLAUSE] [HAVING_CLAUSE] [ORDERBY_CLAUSE]
Klauzule u srednjim zagradama "[ ]" su opcionalne. Update statement se sastoji od slijedećih klauzula:
UPDATE_CLAUSE [WHERE_CLAUSE]
Delete statement se sastoji od slijedećih klauzula:
DELETE_CLAUSE [WHERE_CLAUSE]
Where klauzula za stringove
SELECT x FROM Magazine x WHERE x.title = 'JDJ' OR x.title = 'JavaPro'
Where klauzula za brojčane vrijednosti
SELECT x FROM Magazine x WHERE x.price >= 3.00 AND x.price <= 5.00
Where klauzula za brojčane vrijednosti
SELECT x FROM Magazine x WHERE x.price BETWEEN 3.00 AND 5.00
WILDCARD % kod usporedbe stringova
SELECT x FROM Magazine x WHERE x.title LIKE 'J%'
3.6.1 Ugrađene funkcije
CONCAT(string1, string2): Sastavlja dva stringa.
SELECT x FROM Magazine x WHERE CONCAT(x.title, 's') = 'JDJs'
SUBSTRING(string, startIndex, length): izdvaja podstring. Indeksi počinju od 1 (ne od 0).
SELECT x FROM Magazine x WHERE SUBSTRING(x.title, 1, 1) = 'J'
Java Persistence API sa fokusom na Hibernate v1.0 draft
42
TRIM([LEADING | TRAILING | BOTH] [character FROM] string: Uklanja specificirani karakter. Ako nije specificiran onda se uklanjaju prazni razmaci.
SELECT x FROM Magazine x WHERE TRIM(BOTH 'J' FROM x.title) = 'D'
LOWER(string): string prepravlja na mala slova.
SELECT x FROM Magazine x WHERE LOWER(x.title) = 'jdj'
UPPER(string): string prepravlja na velika slova.
SELECT x FROM Magazine x WHERE UPPER(x.title) = 'JAVAPRO'
LENGTH(string): vraća dužinu stringa
SELECT x FROM Magazine x WHERE LENGTH(x.title) = 3
LOCATE(searchString, candidateString [, startIndex]): Vraća poziciju prvog karaktera pronađenog stringa. Indeksi počinju od 1 (ne od 0).
SELECT x FROM Magazine x WHERE LOCATE('D', x.title) = 2
ABS(number): Vraća apsolutnu vrijednost.
SELECT x FROM Magazine x WHERE ABS(x.price) >= 5.00
SQRT(number): Vraća korijen broja
SELECT x FROM Magazine x WHERE SQRT(x.price) >= 1.00
MOD(number, divisor): Vraća vrijednost modularnog dijeljena.
SELECT x FROM Magazine x WHERE MOD(x.price, 10) = 0
CURRENT_DATE: Vraća trenutni datum. CURRENT_TIME: Vraća trenutno vrijeme. CURRENT_TIMESTAMP: Vraća timestamp.
3.6.2 Relacije
Kretanje kroz relacije je moguće koristeći tačku kao u java sintaksi. Npr. klasa Magazine ima član publisher, a on
ima član name.
SELECT x FROM Magazine x WHERE x.publisher.name = 'Random House'
Isti rezultat, ali pomoću INNER JOIN.
SELECT x FROM Magazine x INNER JOIN x.publisher p WHERE p.name = 'Random House'
Prethodna dva upita vraćaju sve zapise tabele Magazine čiji izdavač ima naziv "Random House". To podrazumijeva da vrijednost x.publisher nije null. Ukoliko je potrebno uključiti null-vrijednost može se koristiti slijedeći upit.
Primjer zapisa čiji publisher može da ima null vrijednost.
SELECT x FROM Magazine x WHERE x.publisher.name = 'Random House' or x.publisher IS NULL
Isti rezultat, ali pomoću LEFT OUTER JOIN.
SELECT x FROM Magazine x LEFT JOIN x.publisher p WHERE p.name = 'Random House'
Java Persistence API sa fokusom na Hibernate v1.0 draft
43
S obzirom da se koriste imena članova unutar klasa potrebno obratiti pažnju na velika i mala slova (x.publisher
nije isto kao x.Publisher). Obično članovi klasa počinju malim slovom (Java konvencija).
3.6.3 Višestruki SELECT izrazi
Redovi upita (rezultat) može da bude:
a) podatak neke poznate entity-klase, npr.
SELECT s FROM Student s
jedan red upita predstavlja tip podataka: Student
b) primitivni podatak npr.
SELECT count(s) FROM Student s
jedan red upita predstavlja tip podataka: long
c) niz objekata "Object[]", npr.
SELECT s.id, s.ime, s.opcina.naziv, COUNT(p.naziv) FROM Student s…
jedan red upita predstavlja tip podataka: int + string + string + long
3.6.4 JPQL primjeri
select order.id, sum(price.amount), count(item)
from Order as order
join order.lineItems as item
join item.product as product,
Catalog as catalog
join catalog.prices as price
where order.paid = false
and order.customer = :customer
and price.product = product
and catalog.effectiveDate < sysdate
and catalog.effectiveDate >= all (
select cat.effectiveDate
from Catalog as cat
where cat.effectiveDate < sysdate
)
group by order
having sum(price.amount) > :minAmount
order by sum(price.amount) desc
Pročitajte detaljniji opis JPQL-a http://docs.jboss.org/hibernate/core/4.0/hem/en-US/html_single/#queryhql
Java Persistence API sa fokusom na Hibernate v1.0 draft
44
3.6.5 Izvršavanje JPQL
Za izvršavanje upita prema JPA 2 specifikaciji mogu se koristiti slijedeća dva Java interfejsa:
[1] Javax.persistence.Query a) upit sa maksimalno jednim zapisom rezultat jeste podatak tipa Object, kojeg je potrebno pretvoriti u odgovarajući tip podataka ("casting" operator)
Query q = em.createQuery("select s from Student s where s.id=1"); Student s = (Student) q. getSingleResult(); Query q = em.createQuery("select count(o) from Opcina o");
Long x = (Long) q.getSingleResult(); b) upit sa 0 ili više zapisa rezultat jeste lista tipa Object
Query q = em.createQuery("select s from Student s"); List<Student> resultList = q.getResultList();
Query q = em.createQuery("select s.id, s.ime, s.prezime from Student s"); List<Object[]> resultList = q.getResultList();
[2] javax.persistence.TypedQuery<T> a) upit sa maksimalno jednim zapisom rezultat je određen parametrom generičke funkcije createQuery (prosljeđuje se podatak tipa Class).
TypedQuery<Student> q = em.createQuery("select s from Student s where s.id=1", Student.class);
Student s = q.getSingleResult();
TypedQuery<Long> q = em.createQuery("select count(o) from Opcina o", Long.class);
Long x = q.getSingleResult();
b) upit sa 0 ili više zapisa rezultat jeste generička lista čiji generički parametar je određena je parametrom funkcije createQuery.
TypedQuery<Student> q = em.createQuery("select s from Student s", Student.class); List<Student> resultList = q.getResultList();
TypedQuery<Object[]> q =
em.createQuery("select s.id, s.ime, s.prezime from Student s", Object[].class); List<Object[]> resultList = q.getResultList();
Interfejs TypedQuery je generički i nasljeđujte interfejs Query.
Java Persistence API sa fokusom na Hibernate v1.0 draft
45
3.6.6 Izvršavanje SQL-a
Za izvršavanje SQL upita koristiti se funkcija createNativeQuery umjesto createQuery te Java interfejs
Javax.persistence.Query. Primjer je dat u nastavku:
a) upit sa maksimalno jednim zapisom rezultat jeste podatak tipa Object, kojeg je potrebno pretvoriti u odgovarajući tip podataka ("casting" operator)
Query q = em.createNativeQuery("select * from Opcina where id=1", Opcina.class); Opcina s = (Opcina) q.getSingleResult();
Query q = em.createNativeQuery("select count(*) from Opcina");
Integer i = (Integer) q.getSingleResult();
Funkcija count u JPQL-u vraća podataka tipa Long, dok istoimena funkcija u SQL-u vraća podataka tipa Integer. b) upit sa 0 ili više zapisa rezultat jeste lista tipa Object
Query q = em.createNativeQuery("select * from Opcina", Opcina.class); List<Opcina> resultList = q.getResultList();
Query q = em.createNativeQuery("select * from Opcina"); List<Object[]> resultList = q.getResultList();
3.6.7 Korištenje parametara
Primjer upita bez parametra čija vrijednost filtera (stringa) je definisana u kodu.
Query q = em.createQuery("select s from Student s where s.ime like 'A%'"); List<Student> resultList = q.getResultList();
ili (bez korištenja varijable q)
List<Student> resultList = em.createQuery(
"select s from Student s where s.ime like 'A%'").getResultList();
Primjer upita bez parametra čija vrijednost filtera (stringa) se učitava sa tastature.
String str = reader.readLine(); // str = "A%"; Query q = em.createQuery("select s from Student s where s.ime like '" + str + "'"); List<Student> resultList = q.getResultList();
ili (bez korištenja varijable q)
String str = reader.readLine(); // str = "A%"; List<Student> resultList = em.createQuery(
"select s from Student s where s.ime like '" + str + "'").getResultList();
Prethodni upit može biti podložan SQL injection napadu. Preporučuje se korištenje parametara. Parametri se u upitima definiraju sa dvotačkom ":nazivParametra". Definisanom paramteru je potrebno dodijeliti neku
vrijednost pomoću funkcije setParameter .
Java Persistence API sa fokusom na Hibernate v1.0 draft
46
Primjer JPQL upita sa parametrom (uz korištenje interfejsa Query)
String str = "A%"; Query query = em.createQuery("select s from Student s where s.ime like :x"); query.setParameter("x", str);
List<Student> resultList = query.getResultList();
Primjer JPQL upita sa parametrom (uz korištenje interfejsa TypedQuery)
String str = "A%"; TypedQuery<Student> query = em.createQuery("select s from Student s where s.ime like :x",
Student.class); query.setParameter("x", str);
List<Student> resultList = query.getResultList();
Primjer SQL upita sa parametrom (uz korištenje interfejsa Query)
String str = "A%"; Query query = em.createNativeQuery("select * from Student s where s.ime like :x",
Student.class); query.setParameter("x", str);
List<Student> resultList = query.getResultList();
3.7 JPA provajderi
Neki od poznatijih provajdera koji implementiraju JPA su:
JPA Framework Reference
Hibernate http://www.hibernate.org
Eclipse Link http://www.eclipse.org/eclipselink/
Toplink http://ww.oracle.com/technetwork/middleware/toplink/overview/index.html
OpenJPA http://openjpa.apache.org/
Tabela 3.3. Pregled JPA provajdera
Hibernate predstavlja najpopularniji JPA provajder. U nastavku slijedi detaljniji opis njegovog konfigurisanja.
Java Persistence API sa fokusom na Hibernate v1.0 draft
47
4. Hibernate
Hibernacija je jedan od najčešće korištenih provajder za mapira Java klasa sa poznatim relacionim baza podataka
što je poznato kao Object-relational mapping - ORM.
4.1 Biblioteke
Hibernate biblioteke se mogu preuzeti sa http://hibernate.org/downloads. Direktan link za preuzimanje Hibernate
v.4.x je slijedeći http://sourceforge.net/projects/hibernate/files/hibernate4/.
JAR fajlovi koji su sadržani u glavnoj arhivi hibernate-release-4.x.x.Final (tgz ili zip) su grupisani na slijedeći način:
Folder Opis
lib\required Ovdje se nalaze svi obavezni JAR fajlovi za korištenje Hibernate-a.
lib\jpa Dodatni JAR fajl za korištenje Hibernate-a kroz JPA specifikaciju.
lib\envers Dodatni JAR fajl korištenje Hibernate Enversa.
- Hibernate Enversa je auditing-alat za čuvanje starijih verzija zapisa u posebnim
tabelama, tj. omogućava pristup starijim verzijama entity klasa
lib\optional Dodatni JAR fajlovi za "connection pool" i "second-level cache".
Kopirajte fajlove iz foldera "lib\required" i "lib\jpa" u novi folder unutar svoje vlastite biblioteke JAR fajlova, kao
npr..:
D:\Moj Eclipse\jars>tree /f
Folder PATH listing for volume xyz
Volume serial number is xyz
D:.
├───Hibernate
│ antlr-2.7.7.jar
│ commons-collections-3.2.1.jar
│ dom4j-1.6.1.jar
│ hibernate-commons-annotations-4.0.1.Final.jar
│ hibernate-core-4.0.1.Final.jar
│ hibernate-entitymanager-4.0.1.Final.jar
│ hibernate-jpa-2.0-api-1.0.1.Final.jar
│ javassist-3.15.0-GA.jar
│ jboss-logging-3.1.0.CR2.jar
│ jboss-transaction-api_1.1_spec-1.0.0.Final.jar
│
└───...
4.2 Konfiguracija
Nakon preuzimanja JAR fajlova i njihovog dodavanja u class-path Java projekta potrebno je konfigurisati postavke provajdera unutar fajla „META-INF\persistence.xml“. Znači, neophodno je napraviti novi folder „META-INF“ te u njega dodati prazan fajl sa imenom „persistence.xml“. Sadržaj fajla persistence.xml možete preuzet iz narednog primjera. Sve postavke iz narednog primjera koje ne počinju sa „java.persistence“ su specifične samo za Hibernate provajder.
Java Persistence API sa fokusom na Hibernate v1.0 draft
48
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="MojaOznaka1" transaction-type="RESOURCE_LOCAL"> <exclude-unlisted-classes>false</exclude-unlisted-classes> <properties> <!-- Podaci o bazi podataka --> <property name="javax.persistence.jdbc.url" value="jdbc:sqlserver://localhost:1433;databaseName=imeBaze" /> <property name="javax.persistence.jdbc.user" value="sa" /> <property name="javax.persistence.jdbc.password" value="test" /> <property name="javax.persistence.jdbc.driver" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" /> <!-- hiberante dialect --> <property name="hibernate.dialect" value="org.hibernate.dialect.SQLServer2008Dialect" /> <!-- JDBC connection pool (use the built-in) --> <property name="connection.pool_size" value="1" /> <!-- Ispis SQL koda u konzolu --> <property name="hibernate.show_sql" value="true" /> <!-- formatiranje SQL koda ispisanog u konzolni prozor --> <property name="hibernate.format_sql" value="false" /> <!-- Vrijednost "update" kreira tabele ako nepostoje --> <property name="hibernate.hbm2ddl.auto" value="update" /> <!-- Enable Hibernate's automatic session context management --> <property name="current_session_context_class" value="thread" /> <!-- Disable the second-level cache --> <property name="cache.provider_class" value="org.hibernate.cache.NoCacheProvider" /> <!-- automatsko prepoznavanje JPA klasa --> <property name="hibernate.archive.autodetection" value="class, hbm" /> </properties> </persistence-unit> </persistence>
Za rad sa Hibernate-om korištenjem defaultnih opcija dovoljno je izmijeniti postavke koje su označene žutom bojom, a to su:
[1] Persistence-unit oznaka
Naziv, koji se ovdje definira, potrebno je navesti prilikom instanciranja klase EntityManagerFactory.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("MojaOznaka1");
[2] JDBC Connection URL
Pogledati tabelu br. 3.
[3] JDBC Username
[4] JDBC Password
[5] JDBC Driver class
Kao što je već prethodno napisano nije potrebno navoditi klasu koja implementira Type 4 drajvere, te se
ovaj tag može izbrisati. Međutim, Hibernate će, ukoliko su aktivirani log-ovi, prikazati poruku upozorenja
iako će raditi potpuno ispravno.
[6] Hibernate Dialect
Ovdje se navodi klasa koja definira Dialect za odabrani DBMS. Dialect govori Hibernate-u koje tipove podataka podržava DBMS (npr. varchar, decimal, datetime2), te koje funkcije podržava DBMS (npr. round). Ukoliko se izostavi tag za dialect, tj. izbriše, hiberate će automatski nakon ostvarivanja konekcije odabrati defaultni dialect za dati DBMS. Međutim, u nekim slučajevima potrebno je ručno navesti dialect koji može da bude jedan od postojećih koje su sadržane u bibliotekama hibernate (tabela 4.1) ili korisničko-definirani dialect. Primjer, kada je potrebno definisati vlastiti Dialect, jeste u slučaju kada želite da defaultne opcije prilikom generisanja tabela budu NVARCHAR umjesto VARCHAR za Java podatak String, što je neophodno za ukoliko želite pohranjivati slova čćžđš itd (dialect za Microsoft Sql Server).
Java Persistence API sa fokusom na Hibernate v1.0 draft
49
[7] hbm2ddl – Hibernate DDL generator Ovdje se navode opcije za generisanje DDL coda prilikom inicijalizirana EntityManagerFactory klase (tj. prilikom pokretanja Java aplikacije). Moguće vrijednosti su:
a. none: Isključena opcija generisanja DDL-a.
b. validate: Generiše DDL koji provjerava da li struktura baze odgovara strukturi entity-klasa. Ova opcije ne vrši nikakvi izmjenu nad bazom.
c. update: Generiše DDL za kreiranje tabela (create table Student…), kolona (alter table Student add column…) i relacija (alter table Student add constraint …). Ukoliko ne postoje tabele, kolone ili relacije, one će bit automatski kreirane. Ukoliko postoje, onda neće biti dodate. Međutim, ukoliko se ukloni neka entity klasa, njeni atributi ili relacije iz Java projekta one neće biti automatski izbrisane iz baze. Brisanje tabela, atributa i relacije u bazi podataka je potrebno ručno izvršiti (putem odgovarajućih klijentskih alata).
d. create: Prilikom pokretanja aplikacije, brišu su sve tabele i podaci te se ponovo kreiraju. Ova opcija služi isključivo radi testiranja.
e. create-drop: Prilkom pokretanja aplikacije kreiraju se tabele, te se brišu prilikom zatvaranja EntityManagerFactory-a. Ovo služi isključivo radi testiranja.
Pregled klasa (sadržanih u glavnom Hibernate JAR fajlu) koje definiraju Hibernate Dialect su date u narednoj tabeli.
DBMS Klasa
DB2 org.hibernate.dialect.DB2Dialect
DB2 AS/400 org.hibernate.dialect.DB2400Dialect
DB2 OS390 org.hibernate.dialect.DB2390Dialect
PostgreSQL org.hibernate.dialect.PostgreSQLDialect
MySQL5 org.hibernate.dialect.MySQL5Dialect
MySQL5 with InnoDB org.hibernate.dialect.MySQL5InnoDBDialect
MySQL with MyISAM org.hibernate.dialect.MySQLMyISAMDialect
Oracle (any version) org.hibernate.dialect.OracleDialect
Oracle 9i org.hibernate.dialect.Oracle9iDialect
Oracle 10g org.hibernate.dialect.Oracle10gDialect
Oracle 11g org.hibernate.dialect.Oracle10gDialect
Sybase org.hibernate.dialect.SybaseASE15Dialect
Sybase Anywhere org.hibernate.dialect.SybaseAnywhereDialect
Microsoft SQL Server 2000 org.hibernate.dialect.SQLServerDialect
Microsoft SQL Server 2005 org.hibernate.dialect.SQLServer2005Dialect
Microsoft SQL Server 2008 org.hibernate.dialect.SQLServer2008Dialect
SAP DB org.hibernate.dialect.SAPDBDialect
Informix org.hibernate.dialect.InformixDialect
HypersonicSQL org.hibernate.dialect.HSQLDialect
H2 Database org.hibernate.dialect.H2Dialect
Ingres org.hibernate.dialect.IngresDialect
Progress org.hibernate.dialect.ProgressDialect
Mckoi SQL org.hibernate.dialect.MckoiDialect
Java Persistence API sa fokusom na Hibernate v1.0 draft
50
Interbase org.hibernate.dialect.InterbaseDialect
Pointbase org.hibernate.dialect.PointbaseDialect
FrontBase org.hibernate.dialect.FrontbaseDialect
Firebird org.hibernate.dialect.FirebirdDialect
Tabela 4.1. Klase koje definiraju Hibernate Dialect
4.3 Hibernate izvan JPA specifikacije
Hibernate se, inače, može koristiti kroz dva okvira:
1. Hibernate (vlastita) specifikacija
2. JPA specifikacija (od verzije Hibernate 3.2, tj. 2007. godine)
U prethodnom poglavlju (tj. 4.2) prikazana je konfiguranje Hibernate-a kroz JPA specifikaciju koja je omogućena tek
od verzije 3.2. Svi primjeri koji su navedeni u poglavlju 3 spadaju u JPA specifikaciju. Međutim, zbog velike
popularnosti Hibernate-a kao ORM-a alata, mnogi programeri i dalje koriste Hibernate kroz okvir Hibernate-
specifikacije koja je postojala i prije definisanja JPA specifikacije. Za rad sa Hibernate-om nije potrebno poznavanje
Hibernate specifikacije, koja se spominje u ovom potpoglavlju. Ali, ipak njeno poznavanje može biti od koristi jer na
internetu postoje mnogi primjeri (tutorijali) koji su prilagođeni ovoj specifikaciji.
Slijedi kratak pregled glavnih razlika u korištenju Hibernate-a između vlastite specifikacije i JPA specifikacije. Razlike
se uglavnom mogu svesti na konfiguracijski fajl, te nazive klasa i funkcija spomenutih u podpoglavlju 3.6 ovog
dokumenta.
Hibernate specifikacija JPA specifikacija
Naziv konfiguracijskog fajla hibernate.cfg.xml META-INF/persistence.xml
Package u kojem su smještene klase org.hibernate.* javax.persistence.*
Klasa odgovorna za ostvarivanje
konekcije Session EntityManager
Klasa odgovorna za učitavanje postavki SessionFactory EntityManagerFactory
Funkcija koja učitava postavke SessionFactory sf = new Configuration().
configure().buildSessionFactory();
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("default");
Funkcija koja kreira klase za sesiju SessionFactory.openSession() EntityManagerFactory.createEntityManager()
Funkcija koja izvršava upit i vraća listu Query.list() Query.getResultList()
Funkcija koja izvršava upit i vraća jedan
objekat Query.uniqueResult() Query.getSingleResult()
Funkcija za snimanje entity-objekta u
bazu Session.save() EntityManager.persist()
Funkcija za prebacivanje entity-objekta
iz stanja detached u stanje merge Session.update() EntityManager.merge()
Funkcija za izvršavanje HQL-a odnosno
JPQL-a Session.createQuery() EntityManager.createQuery()
Funkcija za izvršavanje SQL-a Session.createSQLQuery() Session.createNativeQuery()
Slijedi primjer usporedbe Hibernate i JPA-specifikacije u dijela koda koji dodaje dva objekta u bazu.
Java Persistence API sa fokusom na Hibernate v1.0 draft
51
Hibernate specifikacija JPA specifikacija
EntityManagerFactory f = Persistence.createEntityManagerFactory("default"); EntityManager s = f.createEntityManager(); Student x1 = new Student(); X1.setIme("neko ime 1"); Student x2 = new Student(); X2.setIme("neko ime 2"); s.getTransaction().begin(); s.save(x1); s.save(x2); s.getTransaction().commit(); s.close(); //zatvara se konekcija
SessionFactory f = new Configuration(). configure().buildSessionFactory(); Session s = f.openSession(); Student x1 = new Student(); X1.setIme("neko ime 1"); Student x2 = new Student(); X2.setIme("neko ime 2"); s.getTransaction().begin(); s.save(x1); s.save(x2); s.getTransaction().commit(); s.close(); //zatvara se konekcija
Primjer konfiguracijskog fajla hibernate.cfg.xml je dat u nastavku.
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- Podaci o bazi podataka --> <property name="connection.url">jdbc:sqlserver://localhost:1433;databaseName=imeBaze</property> <property name="connection.username">sa</property> <property name="connection.password">test</property> <property name="connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</property> <!-- Hibernate dialect --> <property name="dialect">org.hibernate.dialect.SQLServer2008Dialect</property> <!-- JDBC connection pool (use the built-in) --> <property name="connection.pool_size">1</property> <!-- Ispis SQL koda u konzolu --> <property name="show_sql">true</property> <!-- Formatiranje SQL koda ispisanog u konzolni prozor --> <property name="format_sql">false</property> <!-- Vrijednost "update" kreira tabele ako nepostoje --> <property name="hibernate.hbm2ddl.auto">update</property> <!-- Enable Hibernate's automatic session context management --> <property name="current_session_context_class">thread</property> <!-- Disable the second-level cache --> <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property> <!-- Lista entity-klasa --> <mapping class="ba.adil.jpa.Demonstrator" /> <mapping class="ba.adil.jpa.KorisnickiNalog" /> <mapping class="ba.adil.jpa.Opcina" /> <mapping class="ba.adil.jpa.Osoba" /> <mapping class="ba.adil.jpa.Predmet" /> <mapping class="ba.adil.jpa.Student" /> </session-factory> </hibernate-configuration>
Java Persistence API sa fokusom na Hibernate v1.0 draft
52
Literatura
[1] Emmanuel Bernard, Steve Ebersole, Gavin King, "Hibernate EntityManager", www.hibernate.org
[2] Juan M. Gimeno, Josep M. Ribo, "Persistence layer. JPA", 2010 [3] Apache OpenJPA 2.3 User's Guide, http://openjpa.apache.org
Popis tabela Tabela 1.1. JDBC drajveri za različite DBMS-ove 5 Tabela 1.2. Neophodne informacije za ostvarivanje konekcije 6 Tabela 1.3. Connection URL formati za različite DBMS-ove 6 Tabela 1.4. Klase koje implementiraju JDBC drajvere 7 Tabela 3.1. Grafički prikaz one-to-one asocijacija u alatu PowerDesigner 21 Tabela 3.2. Grafički prikaz many-to-one i one-to-many asocijacija u alatu PowerDesigner 24 Tabela 3.2. Pregled JPA provajdera 46 Tabela 4.1. Klase koje definiraju Hibernate Dialect 50
Popis slika Slika 1.2. Isključen UAC (Win 7) 12 Slika 1.3. Windows korisnički nalog kao sysadmin na SQL Serveru 12 Slika 1.1. UAC dialoški prozor 12 Slika 1.4. Sql Server autentifikacija + Windows autentifikacija 13 Slika 1.5. Aktiviranje SQL autenfikacije na već instaliranom SQL Serveru 13 Slika 1.6. Aktiviranje System administratora na SQL Serveru (korisnik "sa") 13 Slika 1.7. Protokoli za SQL Server 14 Slika 1.8. Broj porta za TCP/IP protokol na SQL Serveru 14 Slika 1.9. Testiranje pristupa na SQL Server putem TCP/IP protokola 14 Slika 3.1. Asocijacija one-to-one između klase A i klase B (PowerDesigner) 20 Slika 3.2. Asocijacija između klase A i klase B (PowerDesigner) 24 Slika 3.3. Generisana tabela kod nasljeđivanja (Single table mapping) 28 Slika 3.4. Generisana tabela kod nasljeđivanja (Joined-tables mapping) 29 Slika 3.5. Životni vijek objekta pri dodavanju u bazu 32 Slika 3.6. Životni vijek objekta pri učitavanju iz baze 34 Slika 3.7. Životni vijek objekta pri modifikaciji u stanju „Managed“ 37 Slika 3.8. Životni vijek objekta pri modifikaciji u stanju „Detached“ 38 Slika 3.9. Životni vijek objekta pri brisanju u stanju „Managed“ 39 Slika 3.10. Životni vijek objekta pri brisanju u stanju „Detached“ 39 Slika 3.11. Moguće promjene stanja entity-objekata 40