jpa sa fokusom na hibernate (prirucnik, v1.0)

52
mr. sc. Adil Joldić 2012. Java Persistence API sa fokusom na Hibernate Priručnik v1.0 draft

Upload: cbyte84

Post on 04-Jan-2016

263 views

Category:

Documents


15 download

DESCRIPTION

JPA sa fokusom na hibernate, priručnik za rad sa bazama podataka pomocu jave i orm - tehnologije.

TRANSCRIPT

mr. sc. Adil Joldić

2012.

Java Persistence API sa fokusom na

Hibernate

Priručnik

v1.0

draft

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