Upload
hoangthuy
View
274
Download
7
Embed Size (px)
Citation preview
Universitatea Politehnica Bucureşti
Facultatea de Automatică şi Calculatoare
Departamentul de Automatică şi Ingineria Sistemelor
LUCRARE DE LICENŢĂ
Sistem Object Relational Mapping in Java
Bucureşti,2013
Coordonator Şl. Dr. Ing. Ştefan Ioan Sacală
Absolvent George-Alin Dragomir
CUPRINS
Abstract 4
Notaţii şi Abrevieri 5
Listă de figuri 6
Listă de secvenţe de cod 7
1. Introducere 8
1.1 Contextul 8
1.2 ORM (Object Relational Mapping) 9
1.3 Motivatia dezvoltării unui sistem ORM 10
1.4 Descrierea proiectului 11
2. Alegerea Platformei 12
3. Tehnologii folosite 13
3.1 IDE(Mediu integrat de dezvoltare) 13
3.2 Sisteme de management ale bazelor de date relaţionale 16
3.2.1 PostgreSQL 16
3.2.2 Microsoft Sql Server 16
3.3 API-uri folosite 17
3.3.1 API-ul de JDBC(Java Database Connectivity) 17
3.3.2 API-ul de Reflection 18
3.3.3 API-ul de SWT(Standard Widget Toolkit) 19
3.3.4 API-ul pentru crearea de fişiere PDF 21
4. Dezvoltarea aplicaţiei 22
4.1 Cerinţele aplicaţiei 22
4.2 Arhitectura 23
4.3 Structura proiectului 24
4.4. Funcţionalităţile sistemului şi tratarea problemelor 27
5. Testarea aplicaţiei 38
5.1 Fereastra principală 38
5.2 Fereastra Medic 39
5.3 Fereastra Farmacist 43
5.4 Fereastra Pacient 44
6. Concluzii şi Dezvoltări ulterioare 47
6.1 Dezvoltări ulterioare 47
6.2 Concluzii 47
7. Bibliografie 48
ANEXĂ COD 50
Abstract
Proiectul îşi propune a fi o aplicaţie de dezvoltare a unui sistem de ORM (Object Relational
Mapping) care să implementeze cele patru funcţii principale de lucru cu baza de date, select, update,
insert, delete.
El presupune familiarizarea cu API-urile de JDBC şi de Reflection, implementarea mini
ORM-ului şi dezvoltarea unei aplicaţii de test pentru acesta.
Alegerea acestui proiect se datorează lucrului cu astfel de sisteme , dar fără să fi avut o
înţelegere de ansamblu asupra funcţionării interne.
Pentru dezvoltarea aplicaţiei am folosit limbajul de programare Java, API-uri pentru JDBC,
Reflection, SWT şi creare de PDF şi ca sisteme de management al bazelor de date am ales cele mai
reprezentative două sisteme PostgreSQL şi Microsoft Sql Server.
Notaţii şi Abrevieri
API – Application Programming Interface
AWT – Abstract Window Toolkit
DB – Database
GWT – Google Web Toolkit
IDE – Integrated Development Environment
JDBC – Java Database Connectivity
JPA – Java Persistance API
JVM - Java Virtual Machine
J2EE – Java Enterprise Edition
J2ME – Java Micro Edition
J2SE – Java Standard Edition
MVC – Model View Controller
ORM – Object Relational Mapping
PDA – Personal Digital Assistant
PK – Primary Key
POO – Programare Orientată pe Obiecte
SGBD – Sistem de Gestiune a Bazelor de Date
SQL – Structured Query Language
SWT – Standard Widget Toolkit
6
Listă de figuri
Fig. 1.1 ORM –legătura dintre obiecte şi SGBD p9
Fig 2.1. Fluxul de compilare şi rulare al unei clase p12
Fig. 3.1. Meniul Source p14
Fig. 3.2. Fereastră generare getteri/setteri p14
Fig. 3.3. Meniu apărut la apăsarea combinaţiei de taste p15
Fig. 3.4. Paletă de componente p19
Fig. 3.5. Paletă de elemente în SWT p20
Fig. 3.6. Exemplu de casetă cu proprietăţile unui element p20
Fig. 3.7. Clasă pentru dezvoltarea în mediul grafic p21
Fig. 4.1. Arhitectura unei aplicaţii complete ce foloseşte un ORM p24
Fig. 4.2. Organizarea internă a proiectului p24
Fig. 5.1. Fereastra principală p38
Fig. 5.2. Fereastra de categorii p39
Fig. 5.3. Fereastra pentru medici de familie p39
Fig. 5.4. Vizualizarea programărilor p41
Fig. 5.5. Medicină naturistă p42
Fig. 5.6. Medicină Veterinară p42
Fig. 5.7. Farmacist p43
Fig. 5.8. Fereastra pentru programare p44
Fig. 5.9. Mesaj în cazul în care programarea s-a făcut cu success p44
Fig. 5.10. Mesaj în cazul în care programarea nu este posibilă p45
Fig. 5.11. Coloanele tabelului rețete p45
Fig. 5.12. Clasa Reteta ce mapează tabelul retete p46
7
Listă de secvenţe
Secvenţa 4.1.- Date pentru conexiunea la bază p27
Secvenţa 4.2. – Formarea parametrilor şi stabilirea conexiunii p28
Secvenţa 4.3. – Extragerea unei adnotări prezente pe clasă şi studierea parametrului acesteia p28
Secvenţa 4.4. – Extragerea valorii id-ului p29
Secvenţa 4.5. – Studiul adnotării Column pentru maparea unui câmp p30
Secvenţa 4.6. – Obţinerea valorii id-ului p30
Secvenţa 4.7. –Instanţierea unui obiect folosind Reflection p31
Secvenţa 4.8. –Metoda delete(Object) p31
Secvenţa 4.9. – Studiul ordinii coloanelor p32
Sevenţa 4.10. – Metoda save(Long, List) p32
Secvenţa 4.11. – Tratarea obiectelor de tip String şi Date p32
Secvenţa 4.12.- Tratarea ultimei poziţii la update p33
Secvenţa 4.13. – Tratarea ultimei poziţii la insert p33
Secvenţa 4.14. – Metode generice p34
Secvenţa 4.15. – Exemplu de apel metodă generică p34
Secvenţa 4.16. – Extragerea numelui coloanei cu id-ul p34
Secvenţa 4.17. – Formarea listei cu numele coloanelor p35
Secvenţa 4.18. – Exemplu de utilizare listă de nume coloane p35
Secvenţa 4.19. – Generarea id-ului p36
Secvenţa 4.20. – Adnotarea Table p36
Secvenţa 4.21. – Adnotarea PK p37
Secvenţa 4.22. – Adnotarea Column p37
Secvenţa 4.23. – Adnotarea NotPersistent p37
Secvenţa 4.24. – Adnotarea NotNull p37
Secvenţa 4.25. – Adnotarea FK p38
Secvenţa 5.1. – Salvarea unei reţete p40
Secvenţa 5.2. – Exemplu de folosire readAll p41
Secvenţa 5.3. – Exemplu de folosire metoda read p43
8
1. Introducere
1.1 Contextul
Încă de la apariţia modelului de baze de date relaţionale programatorii au simţit
nevoia să sporească posibilităţile acestui model şi să-l facă mai flexibil. Un salt uriaş în acest
sens a fost făcut în anii ’90, atunci când limbajele orientate-obiect au început să capete o
influenţă tot mai mare.
O primă încercare de a lega obiectele din POO cu tabelele unei baze de date a fost
făcută în anul 1992 de compania NeXT (companie înfiinţată de Steve Jobs la mijlocul anilor
’80). Soft-ul dezvoltat nu era un ORM în adevăratul sens al cuvântului, el fiind mai apropiat
de JDBC-ul din Java. Acest sistem purta denumirea de DbKit şi reprezenta un layer de
abstractizare, care asemănător JDBC-ului de astăzi permitea accesul la diferite baze de date
fără obligativitatea de a scrie cod specific bazelor de date. Pornind de la acest sistem,
dezvoltatorii de la NeXT au lansat mai apoi un framework de ORM complet.
Totuşi, primul sistem ce poate fi considerat un ORM a fost dezvoltat pentru limbajul
SmallTalk de o mică firmă din Canada şi s-a numit TopLink. În 1996 a apărut şi prima
versiune pentru Java.
Prima versiune de TopLink permitea printre altele:
a) să mapeze câmpurile bazei de date pe atributele obiectelor
b) să realizeze conversie între tipurile atributelor şi tipurile câmpurilor pe care se
mapează
c) posibilitatea de a mapa o înregistrare din baza de date pe mai multe obiecte
Deşi aparent dezvoltarea unui astfel de sistem pare că a venit de la sine, o perioadă
semnificativă de timp s-a lucrat la rezolvarea conflictelor dintre limbajele orientate-obiect şi
sistemele de gestiune ale bazei de date.
Este cunoscut faptul că o bază de date prezintă pentru fiecare tabel o cheie primară
care identifică în mod unic o înregistrare. Spre deosebire de înregistrările din baza de date,
obiectele din Java pot să fie identice, caz în care au aceaşi valoare şi aceeaşi adresă în
memorie, sau egale, având doar acceaşi valoare.
Alte diferenţe sunt legate de concepte precum moştenire, granularitate, asociere. În
ceea ce priveşte moştenirea, obiectele din Java acceptă moştenire simplă şi preiau atributele şi
metodele obiectului moştenit. În schimb, bazele de date nu cunosc noţiunea de moştenire.
Conceptul de asociere se referă la modul de legare a două entităţi fie în Java, fie în
baze de date. Dacă în Java asocierile sunt unidirecţionale, în baze de date acestea sunt
bidirecţionale şi se fac prin intermediul cheii externe. În cazul în care se doresc legături
bidirecţionale în Java, se poate declara în fiecare obiect un câmp de tipul celuilalt obiect.
Granularitatea se referă la problema legată de numărul de obiecte din model
comparativ cu numărul de tabele din bază. Pentru a se putea face maparea obiectelor pe baza
de date este necesară existenţa unei corespondenţe. Prin urmare cele două numere trebuie să
fie egale.
9
1.2 ORM (Object Relational Mapping)
După cum am precizat, ORM-ul a apărut în perioada în care limbajele de programare
orientate obiect au căpătat un avânt deosebit, fiind principalele limbaje de dezvoltare datorită
flexibilităţii şi uşurinţei de dezvoltare. Cu toate acestea necesitatea comunicării cu un sistem
de gestiune a bazelor de date a determinat piaţa IT să găsească soluţii de comunicare între
obiectele create şi tabelele bazei de date, precum şi găsirea de soluţii pentru conflictele de
incompatibilitate dintre cele două tehnologii.
Astfel, el reprezintă un nivel de legătură între cele cele două modele:
Fig. 1.1 ORM –legătura dintre obiecte şi SGBD
Din figura de mai sus se observă corespondenţa Obiect A – Tabel A , respectiv Obiect B –
Tabel B.
Deşi faptul că asigură comunicarea cu baza de date este un aspect foarte important,
ORM-ul mai are o serie de avantaje precum :
a) Independenţa faţă de baza de date – acest fapt reprezintă un avantaj major al
sistemului ORM deoarece nu contează sistemul de management al unei baze de date
relaţionale folosit, în Java codul folosit va fi acelaşi, metodele nu vor avea alţi parametri sau
nu vor avea alte rezultate. Schimbarea sistemului de management al bazei de date nu va
determina schimbarea vreunei linii de cod în Java.
b) Simplitatea – Este mult mai uşor de lucrat cu obiecte în Java şi de folosit
principiile POO decât lucrul cu cod SQL. În SQL, atunci când se depăşeşte un anumit grad de
dificultate lucrurile pot deveni foarte încurcate, pe când în Java tot timpul vor fi mai uşor de
prelucrat datele şi de folosit aşa cum ne dorim.
c) Scalabilitate – Sistemul va funcţiona la fel indiferent de dimensiunea bazei de
date, a tabelelor sau a tipurilor de date folosite
Obiect A
Obiect B
SGBD ORM
TABEL A
TABEL B
10
d) Rapiditate – Operaţiile dorite se vor executa într-un timp foarte scurt, rezultatele
vazându-se imediat
e) Un mare avantaj pentru programator este reprezentat şi de faptul că nu va mai fi
nevoit să realizeze o conexiune de fiecare dată şi să apeleze la concepte de JDBC, conexiunea
se va face automat când va fi nevoie iar datele vor fi salvate în obiecte.
f) Întreţinerea codului şi depanarea lui va fi foarte uşoară având în vedere cantitatea
de cod mult redusă şi uşurinţa cu care acesta poate fi înţeles.
1.3 Motivaţia dezvoltării unui sistem de ORM
De-a lungul timpului, atât în calitate de student cât şi în calitate de programator, am
avut ocazia să folosesc mai multe sisteme de mapare a obiectelor pe tabelele unei baze de
date. Dintre acestea cele mai cunoscute sunt Hibernate şi JPA(Java Persistence Api).
Deşi ştiam să folosesc un ORM, nu înţelegeam pe deplin tot ce se întâmplă în spate,
iar realizarea acestei lucrări a fost o bună oportunitate să studiez şi să încerc să rezolv
problemele ce pot apărea la un astfel de sistem.
Două probleme pe care eu le-am întâlnit şi pe care le-am considerat mai importante,
necesitând o atenţie mai deosebită erau reprezentate de maparea cheii primare şi
compatibilitatea şirurilor de caractere.
La maparea cheii primare apar de fapt două probleme ce trebuie rezolvate:
Prima din ele este necesitatea specificării printr-un parametru a numelui coloanei din
bază sau numele câmpului trebuie să se potrivească cu numele coloanei din tabel. Prin
soluţia propusă am făcut să nu mai fie necesară potrivirea de nume, am considerat că
tratarea cheii primare ar trebui să fie una specială, în sensul că ar trebui sa fie mai uşor
de mapat, nu mai greu după părerea mea, ca în Hibernate.
A doua problemă la maparea cheii primare este şi cea care face ca maparea să fie mai
dificilă, şi anume generarea id-urilor atunci când se face un insert. În Hibernate practic
trebuia mapată şi o secvenţă pentru generarea id-urilor. De asemenea, neplăceri mai
apăreau şi dacă se făceau insert-uri în sesiuni diferite de lucru,generarea secvenţei
făcându-se de la o valoare cu 50 mai mare faţă de valoarea cu care s-a început
sesiunea precedentă. Astfel, dacă se făceau într-o sesiune de lucru mai mult de 50 de
insert-uri apăreau probleme în sesiunea următoare deoarece se încerca o inserare cu un
id care deja exista. Eu am încercat ca prin simpla mapare cu o adnotare generarea să se
facă automat în raport cu valoarea maximă din tabel indiferent de sesiunea de lucru.
La compatibilitatea şirurilor de caractere, Hibernate impune o limitare de 255 de
caractere. Dacă se folosesc şiruri de caractere mai mari, este necesar la mapare să se
precizeze şi dimensiunea şirului. Am făcut ca şirurile de caractere să fie compatibile fără a
fi nevoie de o mapare specială în cazul unor dimensiuni mai mari.
11
Deşi am utilizat mai multe astfel de sisteme de ORM, am ales să mă raportez la Hibernate
deoarece este cel mai popular dintre ele şi totodată cel mai complet sistem de acest gen
existent pe piaţă, acesta fiind şi sistemul cu care eu m-am iniţiat în tainele folosirii ORM-
urilor.
1.4. Descrierea proiectului
Proiectul presupune dezvoltarea unui mini sistem de mapare a obiectelor pe tabelele
din baza de date. Totuşi el trebuie să permită maparea obiectelor pe orice sistem de
management al bazelor de date relaţionale, fără impunerea unui anumit sistem. Totodată, el
trebuie să poată să facă conversie între tipurile proprietăţilor din obiectele Java şi tipul
câmpurilor din tabele.
Acest mini ORM trebuie să implemeteze principalele operaţii pe baza de date,
cunoscute în limbajul SQL ca: SELECT, DELETE, UPDATE, INSERT. Metodele asociate
acestor operaţii sunt:
pentru SELECT
a) read(Object) –această metodă primeşte ca parametru un obiect al cărui id este
cunoscut şi returnează din baza de date valorile câmpurilor pentru înregistrarea
cu id-ul id
b) readAll(Object) – această metodă primeşte ca parametru un obiect care
corespunde unui tabel din bază şi returnează toate înregistrarile din respectivul
tabel.
pentru DELETE
delete(Object) – această metodă primeşte ca parametru un obiect cu id
cunoscut şi şterge din bază înregistrarea din tabelul asociat obiectului, care are
id-ul id.Această metodă nu returnează nici un rezultat.
pentru UPDATE şi INSERT
save(Object) – această metodă primeşte ca parametru obiectul care trebuie
salvat. Această metodă reprezintă de fapt saveOrUpdate pentru că în urma
analizării id-ului obiectului el ştie dacă să facă Update sau să facă Insert. Astfel
dacă id-ul obiectului este null, ceea ce înseamnă că obiectul nu conţine date, se
va face un insert în bază. În caz contrar se va face un Update.
Proiectul presupune rescrierea de la capăt a metodelor de lucru pe bazele de date şi
nu doar apelarea unor metode existente din alte sisteme asemănătoare.
În procesul dezvoltării acestui sistem, dezvoltatorul este nevoit să facă apel la noţiuni
precum:
a) Reflection pentru studierea conţinutului claselor
b) JDBC – pentru realizarea conexiunilor cu baza de date
Pentru a demonstra avantajul sistemului este neesară realizarea unei aplicaţii de test
care să permită tipuri de date multiple. Această aplicaţie are ca tematică medicina şi se ocupă
12
în principiu de reţete medicale, programări la doctor şi vizualizarea acestora şi este dezvoltată
folosind API-ul de SWT.
2. Alegerea Platformei
O platformă este un mediu hardware sau software în care poate fi rulat un program.
Platforma aleasă pentru dezvoltarea ORM-ului este platforma Java. Aceasta este o platformă
software.
Alegerea acestei platforme se justifică prin multitudinea de avantaje pe care le pune la
dispoziţia dezvoltatorului:
este portabilă,
rulează pe orice sistem de operare(Windows, Linux , Solaris, Mac OS) datorită
interpretorului integrat ce furnizează cod nativ al respectivului sistem
Alte motive sunt reprezentate de faptul că limbajul Java este orientat obiect ceea ce permite
refolosirea codului şi duce la micşorarea semnificativă a dimensiunii codului, este robust şi nu
în ultimul rând este simplu de utilizat.
Platforma Java are două componente:
a) Maşina virtuală Java (Java Virtual Machine) – Este componenta platformei
care face posibilă neutralitatea faţă de arhitectura folosită. La compilarea fişierelor .java ce
conţin codul sursă se creează nişte fişiere cu extensia .class care conţin secvenţe de bytecodes.
Java bytecodes reprezintă instrucţiuni în limbaj maşină pentru JVM (Java Virtual Machine).
La stadiul actual instrucţiunile nu sunt încă destinate procesorului. Pentru a putea fi
transformate în cod înţeles de procesor este necesară o tranformarea din bytecodes în cod
nativ al platformei hardware prin intermediul Interpretorului integrat în structura JVM
Fig 2.1. – Fluxul de compilare şi rulare al unei clase
b) A doua componentă a platformei Java este API – ul de Java (Application
Programming Interface) – Acesta reprezintă o colecţtie de librării ce ajută la dezvoltarea mai
uşoară a programelor. API-ul de Java este structurat în pachete, fiecare pachet înglobând
metode, constante, parametri utilizaţi de către programatori în crearea obiectelor şi structurilor
proprii.
În continuare voi dezvălui câteva avantaje ce justifică alegerea unei astfel de
platforme. Ele sunt după cum urmează:
Clasă.java
Compilare
Clasă.class(
bytecodes) JVM
Cod maşină
13
a) Simplitatea – Java a fost proiectat să fie un limbaj simplu de utilizat şi înţeles, el poate
fi asimilat cu usurinţă de către programatori, mai ales dacă aceştia au ceva experienţă si cu
C++.
b) Java este un limbaj orientat obiect – Oferă un grad ridicat de abstractizare şi
încapsulare. Aest lucru duce la performanţe de securitate crescute, programatorul putând face
vizibile doar informaţiile pe care le doreşte din interiorul obiectelor şi folosind pentru acest
lucru metode dedicate accesării informaţiilor.
c) Lucrul cu memoria este mult îmbunătăţit, acest lucru datorându-se în mare parte
faptului că nu mai există pointeri definiţi de programator. Aceştia erau o sursă bogată de erori
atunci când se făcea accesul la memorie în C/C++. Limbajul introduce de asemenea un sistem
automat numit Garbage Collector care urmăreşte structura memoriei şi observă obiectele
neutilizate urmând să elibereze zona ocupată de respectivele obiecte şi evitând astfel
fragmentarea memorie.
d) Principalul avantaj îl reprezintă portabilitatea. – Un cod scris corect în limbajul Java ar
trebui să ruleze pe orice platformă hardware fără nici o problemă. Acesta reprezintă un
avantaj major chiar şi în faţa unor limbaje care pot rula mai repede aşa cum este C/C++. Este
adevărat că Java consumă mai multă memorie şi rulează într-un timp puţin mai mare decât
C/C++ dat fiind faptul că este atât un limbaj compilat cât şi un limbaj interpretat. Totuşi în
ziua de azi diferenţele nu mai sunt aşa mari ca la începuturile limbajului Java, iar
portabilitatea limbajului Java eclipsează micul avantaj al C++. În cazul în care un program
dezvoltat în C++ ar trebui portat pe altă platformă ar fi necesare modificări majore în cod.
În dezvoltarea ORM – ului am ales platforma Java Standard Edition din cele trei
platforme oferite de Java, celelale două fiind Java Micro Edition (platformă destinată
dispozitivelor mobile aşa cum sunt PDA – uri sau telefoane mobile) şi Java Enterprise Edition
(platformă destinată sistemelor de calcul distribuite sau de mari dimensiuni), deoarece se
pretează cel mai bine cerinţelor proiectului fiind o platformă specifică aplicaţiilor desktop.
3. Tehnologii folosite
În dezvoltarea acestui ORM am folosit:
un IDE(Mediu Integrat de Dezvoltare),
două sisteme de management ale bazelor de date relaţionale
patru API–uri necesare dezvoltării sistemului și testării acestuia.
3.1. IDE (Mediu integrat de dezvoltare)
Ca mediu integrat de dezvoltare am folosit Eclipse versiunea 4.2.2 (Eclipse Juno) care
poate fi descărcată gratuit de pe site – ul cu acelaşi nume. Am făcut această alegere deoarece
este un IDE foarte popular care oferă numeroase facilităţi cum ar fi generarea automată de
cod, formatarea automată a codului şi folosirea scurtăturilor de taste pentru îndeplinirea mai
multor acțiuni precum importuri de pachete necesare.
14
Cele mai folosite faciliăţi din cele amintite mai sus sunt generarea automată a codului
şi realizarea importurilor necesare. Generarea automată de cod se referă în general la
generarea de constructori cu sau fără parametri și generarea de getteri și setteri. Există două
modalităţi de generare a getteri – lor și setteri – lor:
a) Din meniul Source se alege opţiunea „Generate Getters and Setters...”. În
fereastra deschisă se pot alege câmpurile pentru care să se genereze getteri şi setteri.
Fig. 3.1. –Meniul Source
b) A doua metodă foloseşte combinaţia de taste ALT+S după care se apasă tasta „R”.
După apăsarea tastelor se va deschide aceeaşi fereastră ca la punctul anterior.
Fig. 3.2. – Fereastră generare getteri/setteri
15
De asemenea există două modalităţi de generare a constructorilor:
a) Din meniul Source se alege una din opţiunile „Generate Constructor using
fields...” sau „Generate Constructors from Superclass...”. În primul caz se pot alege câmpurile
ce vor fi introduse în construcor, iar în cel de – al doilea se generează un constructor moştenit
de la clasa Object.
b) A doua metodă foloseşte combinaţia de taste ALT+S şi după aceea se apasă
tasta C. Prin această combinaţie se deschide o listă similară cu cea a meniului Source din care
dezvoltatorul poate alege opţiunea cea mai convenabilă.
Fig. 3.3. – Meniu apărut la apăsarea combinaţiei de taste
Realizarea importurilor pachetelor se face prin combinaţia de taste
CTRL+SHIFT+O. După apăsarea lor, Eclipse va face toate importurile necesare în clasa în
care ne aflăm. O altă combinaţie de taste utilă este CTRL+SHIFT+F, combinație ce realizează
formatarea codului.
În cazul unei erori de cod, precum greşirea numelui unei variabile sau atunci când nu
se cunosc toţi parametri unei metode se poate folosi combinaţia de taste CTRL+1 (Quick Fix)
care îţi afişează o listă cu soluţii posibile la problemă.
Un alt avantaj al folosirii acestui IDE îl reprezintă multitudinea de plug–in–uri
disponibile gratuit ce vin în ajutorul dezvoltatorului. Din cele mai importante amintim plug-
in–ul pentru dezvoltarea de aplicații pe platforma Android, plug-in-ul pentru interfețe web de
la google(GWT), plug-in-ul pentru SWT.
16
3.2. Sisteme de management ale bazelor de date relaționale
Am ales ca sisteme de management ale bazelor de date relaționale PostgeSQL și
Microsoft SQL Server 2012 deoarece sunt printre cele mai importante și utilizate sisteme de
acest gen.
3.2.1 PostgreSQL
PostgreSQL este probabil cel mai apreciat sistem de management al bazelor de date
relaționale datorită faptului că este disponibil pentru toate marile platforme(Windows, Linux,
Mac OS X) și este de asemenea apreciat pentru stabilitate, integritatea datelor și corectitudine.
Oferă posibiliatea stocării de imagini, sunete și chiar clipuri video. De asemenea PostgreSQL
este compatibil și cu PL/SQL de la Oracle. Succesul se datorează și faptului că sistemul este
gratuit și open-source.
Un alt avantaj al PostgreSQL este faptul că oferă interfețe de lucru cu numeroase
limbaje de programare așa cum sunt Java, C++, .NET, PHP, Perl etc.
Lucrul ce a determinat ca PostgreSQL să fie cel mai apreciat astfel de sistem atât de
către mari companii cât și de utilizatorii de zi cu zi este reprezentat de limitările pe care
sistemul le impune: dimensiunea unui tabel poate atinge 32TB, dimesiunea unei înregistrări
1,6 TB, numărul de înregistrari poate fi nelimitat, dimesiunea bazei de date poate fi
nelimitată, numărul de coloane poate ajunge la 1600 în funcție de tipul de date și nu în ultimul
rând numarul de indecși pentru un tabel este nelimitat.
3.2.2 Microsoft SQL Server
Microsoft SQL Server este un sistem de management al bazelor de date relaționale
dezvoltat de Microsoft. Având în vedere popularitatea sistemului de operare al celor de la
Microsoft și sistemul de management al bazelor de date relaționale dezvoltat de ei este unul
dintre cele mai populare, unul din dezavantaje fiind că este disponibil doar pentru sistemele de
operare de la Microsoft. Sistemul nu este open-source, există unele variante gratuite dar la
care se limitează dimensiunea bazei de date la 10 GB. Pentru o variantă plătită dimensiunea
bazei de date este de 540 PB.
Un motiv pentru care Microsoft SQL Server este popular deși este funcțional doar pe
sistemele de operare de la Microsoft sunt limitările pe care le impune, pe lângă dimesiunea
bazei de date mai sus amintită sistemul impune următoarele limitări: dimesiunea unei
înregistrări este nelimitată, numărul de coloane pentru fiecare tabel poate ajunge la 30000,
numărul maxim de caractere al numelui unei coloane este de 128, iar dimesiunea maximă a
unui tabel corespunde cu dimensiunea maximă a bazei de date.
Un alt avantaj al Microsoft SQL Server este integrarea cu celelalte unelte software
dezvoltate de Microsoft, care sunt foarte populare, astfel o persoană sau o companie care
utilizează software Microsoft nu va avea nevoie de modificări mari sau de upgrade-uri ale
sistemelor deja existente atunci când vor dori instalarea acestui sistem de management al
bazelor de date. Microsoft SQL Server oferă interfață de lucru cu toate limbajele suportate de
platforma .NET cum ar fi: C#, J#, Visual Basic, C++, F# etc.
În afara limbajelor suportate de platforma .NET Microsoft SQL Server mai oferă
interfață de lucru cu Java, cu ajutorul unui driver de JDBC, aceasta fiind o tehnologie pe care
o vom trata mai târziu.
17
Am ales aceste două sisteme deoarece le-am considerat reprezentative pentru
sistemele de management ale bazelor de date existente.
3.3. API-uri folosite
În dezvoltarea acestui ORM am folosit patru API-uri: API-ul de JDBC oferit de
platforma Java, API-ul de Reflection, API-ul de SWT(Standard Widget Toolkit) şi API-ul
pentru generarea de PDF-uri.
3.3.1 API-ul de JDBC(Java Database Connectivity)
JDBC este un API dezvoltat de compania Oracle care ajută la lucrul cu o bază de
date scriind cod Java. Acest API are suport pentru executarea a diferite operații pe baza de
date cum ar fi INSERT, UPDATE, DELETE, SELECT.
În continuare voi prezenta modul de lucru cu JDBC și pașii necesari extragerii unui
set de date din bază sau executării oricărei alte operații pe baza de date. Vom presupune că
avem deja o bază de date formată.
Mai întâi de toate este necesară descărcarea driver-ului specific bazei de date pe care
o folosim și includerea lui în proiectul nostru.
Următorul pas este încărcarea driver-ului din cod cu ajutorul metodei
forName(„numele driverului”) aplicată pe un obiect Class în felul următor:
Class.forName(„numele driverului”). Aceasta metodă va căuta driver-ul, iar dacă acesta nu
există va genera o excepţie corespunzătoare.
În continuare este necesară stabilirea conexiunii cu baza de date. Pentru acest lucru
va trebui să cunoaştem:
utilizatorul bazei de date,
parola pentru accesul la baza de date
url-ul bazei de date împreună cu numele acesteia.
Stabilirea conexiunii se face prin aplicarea metodei getConnection pe un obiect de tip
DriverManager în felul următor:
DriverManager.getConnection(url, utilizator, parolă) în cazul majorităţii sistemelor
de management ale bazelor de date aşa cum este şi PostgreSQL,
pentru Microsoft SQL Server se foloseşte metoda getConnection( şir ), care după
cum se poate observa primeşte un singur parameru de tip String care conţine toate
datele necesare stabilirii conexiunii. Această metodă va returna un obiect de tip
Connection. Dacă conexiunea nu se realizează se va genera o excepţie în
concordanţă cu problema apărută.
După stabilirea conexiunii vom scrie interogarea pentru acţiunea ce urmează să o
executăm pe baza de date. Aceasta va fi reţinută într-o variabilă de tip String. Pentru
executarea interogării sunt necesari doi paşi.
18
În primul rând este necesară crearea unui obiect de tip Statement pe baza conexiunii
deja create. Pot exista trei tipuri de acest fel:
Statement care atunci când este folosit încarcă în memorie de fiecare dată
interogarea,
PreparedStatement care încarcă o singură dată interogarea în memorie şi o
păstrează acolo,
CallableStatement care este folosită atunci când se apelează proceduri stocate
ale bazei de date.
În dezvoltarea proiectului am folosit un obiect de tip Statement. Apoi vom aplica
obiectului de tip Statement metoda executeQuery care va primi ca parametru variabila de tip
String în care am reţinut interogarea.Dacă s-a făcut un SELECT rezultatele vor fi reţinute
într-un obiect de tip ResultSet. În cazul în care operaţia a fost una de tip UPDATE, INSERT
sau DELETE vom primi ca rezultat doar numărul de rânduri afectate de către operaţia noastră.
De asemenea cu ajutorul JDBC putem obţine informaţii si despre tabele, cum ar fi
numele coloanelor, tipul lor, numărul lor. Acest lucru se poate realiza folosind metoda
getMetaData() pe care o aplicăm pe un obiect de tip ResultSet. Meta datele sunt reţinute într-
un obiect de tip ResultSetMetaData.
Ultimul pas după ce ne-am îndeplinit scopul propus este închiderea conexiunii prin
aplicarea metodei close() asupra obiectului de tip Connection în care am reţinut conexiunea.
În cazul în care uităm să închidem conexiunea Garbage Collector-ul va închide respectiva
conexiune când eliberează memoria ocupată de obiecte ce nu mai sunt folosite. Totuşi nu se
încurajează lăsarea acestei probleme în seama collector-ului deoarece pot apărea alte
probleme.
3.3.2. API-ul de Reflection
Reflection este o tehnologie ce permite studiul structurii unui obiect şi modificarea
acesteia sau luarea de decizii în timpul rulării pe baza structurii din acel moment a obiectului.
În Java API-ul de Reflection permite studierea clasei unui obiect, a câmpurilor
acestuia, a constructorilor, a metodelor din clasă. Este posibilă aflarea de informaţii chiar şi
despre câmpurile private.
Despre clase putem afla informaţii cum ar fi adnotările care sunt folosite pentru clasa
respectivă, numele clasei, câmpurile clasei, metodele acesteia, constructorii acesteia, pachetul
din care face parte şi pachetele importate, interfeţele pe care le implementează.
Despre constructorul unei clase putem afla parametrii pe care acesta îi primeşte şi
putem crea noi instanţe ale obiectului studiat prin apelul metodei newInstance(). Aceasta se
aplică pe obiectul de tip Constructor în care am reţinut constructorul obiectului studiat.
Această metodă primeşte ca parametri exact ce parametri primeşte şi constructorul obiectului
studiat.
Atunci când vrem să aflăm informaţii sau să manipulăm câmpurile putem folosi
Reflection pentru a afla numele câmpurilor, adnotările ce sunt folosite pentru câmpul studiat,
tipurile acestora şi putem afla şi modifica valorile acestora.
19
Pentru a afla informaţiile despre câmpurile private este necesar să folosim metoda
getDeclaredFields().Aceasta, spre deosebire de metoda getFields() care ne va returna un
vector ce va conţine doar câmpurile publice, ne va returna un vector cu toate câmpurile clasei
inclusiv cele private.
Despre metode putem afla parametri pe care aceasta îi primeşte şi tipul pe care
aceasta îl returnează. De asemenea cu ajutorul Reflection putem executa metode ale obiectului
cu ajutorul metodei invoke() care va primi ca parametri obiectul pentru care se execută
metoda şi parametrii pe care aceasta îi primeşte. Metoda invoke() va fi executată pentru
obiectul de tip Method în care am reţinut metoda.
Mai putem afla informaţii cum ar fii adnotările prezente pe parametri funcţiilor sau ai
constructorilor, adnotările de pe getteri şi setteri.
3.3.3. API-ul de SWT(Standard Widget Toolkit)
Standard Widget Toolkit este un plug-in destinat dezvoltării de interfeţe grafice.
Acest plug-in foloseşte componente native ale sistemului de operare pe care se face
dezvoltarea însă elementele grafice nu au un aspect învechit aşa cum ne-am aştepta. SWT se
vrea a fi o combinaţie între rapiditatea cu care sunt încărcate elementele native la AWT şi
aspectul plăcut al elementelor din Swing.
Dezvoltarea se poate face atât din mediul grafic (drag and drop dintr-o paletă de
elemente în fereastra noastră ) cât şi din cod. Varianta preferată de cei mai mulţi dintre
programatori este dezvoltarea în mediul grafic, rămânând ca în cod sa fie scrise doar
ascultătorii petru butoane şi celelalte elemente
grafice folosite.
Fig. 3.4. - Paletă de componente
20
Fig. 3.5. - Paleta de elemente în SWT
Fig. 3.6. – Exemplu de casetă cu proprietăţile unui element
Prin adăugarea unui element din paletă în canvas, se va deschide automat o fereastră
de proprietăţi din care poate fi ajustat design-ul elementului respectiv.
21
Pentru a putea folosi SWT este necesară instalarea plug-in-ului mai întâi deoarece
acesta nu este prezent în pachetul standard al Eclipse. Acest lucru se poate face foarte uşor din
Eclipse din meniul Help opţiunea „Install New Software...”. Plug-in-ul este gratuit şi open-
source. Plug-in-ul este dezvoltat de IBM şi de aceea nu vine împreună cu API-ul standard de
java, în API-ul standard de Java putem găsi Swing şi AWT, care sunt dezvoltate de compania
Sun, companie ce deţine drepturile asupra Java.
Pentru a putea folosi mediul grafic de dezvoltare este necesară crearea unui tip de
clasă ce poate fi găsit după instalarea plug-in-ului. Crearea acestui tip de clasă se face prin
click dreapta pe pachetul în care vrem să creem clasa, din meniul deschis alegem opţiunea
„New”, iar din submeniul deschis vom alege opţiunea „Other”. Din fereastra deschisă vom
alege tipul de clasă Application Window din folderul SWT aşa cum veţi vedea în imaginea
următoare.
Fig. 3.7. – Clasă pentru dezvoltarea în mediul grafic
3.3.4. API-ul pentru crearea de fişiere PDF
Pentru crearea de fişiere PDF am folosit API-ul iText. iText este o bibliotecă ce
permite crearea, citirea şi manipularea de fişiere PDF. Pentru instalarea lui este necesară
decărcarea arhivei de tip jar ce conţine biblioteca şi introducerea ei în proiect.
iText oferă numeroase facilităţi de formare a PDF-urilor:
a) se poate face gruparea mai multor elemente într-un paragraf sau într-o frază,
b) se pot seta fonturi pentru diverse elemente cum ar fi un titlu sau un paragraf,
c) se pot adăuga imagini, tabele,
d) se pot pune referinţe,
e) se poate face împărţirea pe capitole şi secţiuni.
f) de asemenea se poate scrie cu indici sau cu exponent sau se poate scrie textul
subliniat, tăiat, boldat sau înclinat.
22
iText are elemente cu o structură ierarhică. Cea mai mică unitate din iText se
numeşte Chunk. Chunk-ul reprezintă un String căruia i se poate seta doar fontul. Chunk-urile
se pot organiza în structuri mai mari şi anume obiecte de tip Phrase.
Obiectele de tip Phrase permit definirea de spaţiere. Pentru definirea mai multor
atribute cum ar fi cele de stil se folosesc obiecte de tip Paragraph. Acestea sunt obiecte ce
moştenesc obiectele de tip Phrase.
O clasă de obiecte ce moşteneşte clasa Paragraph se numeşte Anchor şi permite
definirea de referinţe de la anumite porţiuni din text la alte porţiuni mai îndepărtate sau la care
se face referire în text. Obiectele de tip Paragraph se pot grupa în structuri de tip Chapter.
În iText mai putem întâlni obiecte de tip Section pentru definirea unei secţiuni, List
pentru realizarea de liste ordonate sau neordonate, obiecte de tip Font penru definirea de
fonturi.
4. Dezvoltarea aplicaţiei
Aplicaţia dezvoltată este reprezentată de un mini sistem de mapare a obiectelor din
Java pe tabelele unei baze de date, sistem denumit Object-Relational Mapping.
4.1. Cerinţele aplicaţiei
Scopul acestei lucrări îl reprezintă dezvoltarea unui sistem ORM care să fie uşor de
folosit şi înţeles.
Obiectivele acesti proiect sunt reprezentate de implementarea operaţiilor uzuale de
INSERT, UPDATE, DELETE, SELECT, iar mai apoi să poată fi folosite fără a mai scrie cod
SQL.
Un alt obiectiv îl reprezintă funcţionarea ORM-ului pe baza unor relaţii stabilite între
obiectele din Java şi tabelele din baza de date. Aceste relaţii se pot stabili fie prin folosirea
adnotărilor fie prin folosirea unor fişiere de configurare de tip properties sau xml. În această
lucrare am ales să folosesc varianta cu adnotări deoarece este varianta mai modernă şi care a
câştigat teren în faţa celei de-a doua în ultimii ani. De asemenea varianta cu asdnotări este
după părerea mea mai simplu de înţeles şi mai intuitivă.
După definirea relaţiilor dintre obiecte şi tabele se vor folosi doar obiectele şi
metodele implementate în ORM. ORM-ul trebuie să dispună de următoarele metode:
a) read(object) – citeşte toate câmpurile obiectului specificat din care iniţial este
cunoscut doar id-ul
b) readAll(object) – citeşte toate obiectele de tipul obiectului specificat fără vreo
clauză WHERE
c) delete(object) – şterge obiectul specificat din care iniţial este cunoscut doar id-ul
d) save(object) – dacă id-ul este null se va face un INSERT în baza de date, se vor
salva câmpurile obiectului specificat şi se va genera un id; dacă id-ul nu este null atunci se va
face un UPDATE în baza de date
23
Conexiunea trebuie să fie realizată cu ajutorul unui fişier de configurări. Datele se
vor introduce o singură dată iar apoi pe baza lor conexiunea se va face automat.
4.2. Arhitectura
Noţiunea de arhitectură software se referă la componentele de nivel înalt ale unui
sistem software. Arhitectura defineşte atât modulele sistemului pe baza unui set de reguli şi
relaţiile care se stabilesc între acestea, cât şi proprietăţile pe care modulele şi relaţiile dintre
ele le deţin.
La cazul general, o arhitectură pentru un sistem software se compune din:
Componentă de comunicaţie cu exteriorul
Componentă ce se ocupă de logica de business a aplicaţiei
Componentă responsabilă cu păstrarea datelor
Componenta de comunicaţie cu exteriorul poate fi reprezentată în funcţie de tipul
aplicaţiei de pagini web, interfeţe grafice desktop ş.a. Prin intermediul acesteia utilizatorul
introduce date în sistem sau preia date din sistem.
Componenta responsabilă cu prelucrarea informaţiilor provenite de la utilizator sau a
informaţiilor ce îi vor fi returnate acestuia este componenta de logică de business şi reprezintă
partea centrală a aplicaţiei. Toate datele care circulă prin sistem sunt întotdeauna filtrate de
această componentă.
Componenta responsabilă de păstrarea informaţiilor în sistem este reprezentată de un
sistem de management al bazelor de date. Aceasta permite salvarea unei cantităţi mari de date,
preluarea datelor , actualizarea sau ştergerea acestora.
O astfel de arhitectură am folosit şi în dezvoltarea şi testarea proiectului de faţă. În
cazul acestui proiect am considerat pentru componenta de baze de date cele mai utilizate două
sisteme de management ale bazelor de date,PostgreSQL şi Microsoft SQL Server.
Componenta care se ocupă de comunicaţia cu exteriorul este reprezentată aici de o
aplicaţie grafică în care utlizatorul interacţionează cu restul aplicaţiei prin intermediul unor
formulare. Totuşi, această componentă poate fi reprezentată la fel de bine de orice altă
aplicaţie ce foloseşte o conexiune la baza de date.
Componenta de logică de business este reprezentată aici de ORM. Sistemul ORM
funcţionează ca o legătură între aplicaţie şi baza de date. Prin intermediul lui se preiau datele
de la aplicaţia grafică, pe care mai apoi le folosim pentru a accesa baza de date.Totodată el
permite salvarea informaţiilor introduse prin aplicaţie în baza de date sau poate extrage
informaţii din bază în funcţie de acţiunile făcute de utilizator. După preluarea datelor, ORM-
ul, le prelucrează şi le face disponibile pentru aplicaţie.
În imaginea de următoare am ilustrat arhitectura unui sistem complet din viaţa reală
în care se foloseşte un ORM.
24
Fig. 4.1. - Arhitectura unei aplicaţii complete ce foloseşte un ORM
4.3 Strucura proiectului
Codul proiectului este organizat pe pachete în funcţie de funcţionalităţile pe care le
oferă.
Fig. 4.2. - Organizarea internă a proiectului
Bază de date
(PostgreSQL,
Microsoft Server
SQL, etc.)
Sistem Object-Relational
Mapping
(Insert, Update, Delete,
Read, Mapare tabele)
Aplicaţie , Interfaţă cu
utilizatorul
25
Pachetul org.licenta.miniorm.connection se ocupă de realizarea legăturii cu baza de
date. Acest pachet conţine 3 fişiere:
connection_data.properties - conţine informaţiile necesare unei conexiuni cu baza de
date, nume bază, driver-ul pentru baza respectivă, nume utilizator şi parola acestuia
pentru conectare
DataConn.java – se ocupă de parsarea elementelor din fişierul properties
ConnectionDB.java –se ocupă de preluarea informaţiilor parsate de DataConn şi
stabilirea conexiunii cu baza
Pachetul org.licenta.miniorm.queryactions conţine clasa QueryActions.java care se
ocupă de implementarea operaţiilor SELECT, UPDATE,DELETE, INSERT pe baza de date
dar cu ajutorul jdbc-ului.
Pachetul org.licenţa.miniorm.annotations se ocupă de crearea tipurilor de adnotări.
Acesta conţine:
Table.java – defineşte structura adnotării pentru maparea obiectului pe tabel;
PK.java – defineşte structura adnotării pentru maparea id-ului obiectului pe cheia
primară din tabelul asociat, specificat de adnotarea anterioară;
Column.java – defineşte structura adnotării pentru maparea unei coloane oarecare pe
un câmp al tabelului asociat din bază;
NotPersistent.java – defineşte structura adnotării care se asigură că un câmp nu va fi
mapat pe o coloană din bază;
NotNull.java –defineşte structura adnotării care se asigură că o proprietate din obiect
nu poate fi nulă.
Pachetul org.licenta.miniorm.ormhandler conţine clasa OrmHandler.java care
implementează metodele read, readAll, delete, save utilizând metodele create în clasa
QueryActions.java şi parsând obiectul pentru obţinerea adnotărilor.
Acest pachet împreună cu pachetul de queryactions reprezintă centrul propriu zis aplicaţiei
şi ocupându-se de ceea ce înseamnă un orm.
Pachetul org.licenta.aplicatie.model conţine clasele model folosite pentru testarea
orm-ului dezvoltat:
Medic.java – este o clasă model ce conţine câmpurile:
a) idMedic – este de tip Long şi prezintă adnotarea PK
b) idCategorie – este de tip Long şi prezintă adnotarea Column; el face
legătura cu obiectul Categorie
c) numeMedic – este de tip String şi prezintă adnotarea Column
d) codIdentificare – este de tip String şi prezintă adnotarea Column
26
Categorie Medic.java – este o clasă model pentru categoriile de medicină existentă şi
conţine câmpurile:
a) idCategorie – este de tip Long şi prezintă adnotarea PK
b) denumireCategorie – este de tip String şi prezintă adnotarea Column
Reteta.java –este o clasă model ce descrie câmpurile unei reţete medicale:
a) idReteta – este de tip Long şi prezintă adnotarea PK
b) idMedic – este de tip Long şi prezintă adnotarea Column; el face legătura
cu obiectul Medic
c) numePacient – este de tip String şi prezintă adnotarea Column
d) nrStrada – este de tip Long şi prezintă adnotarea Column
e) câmpurile varsta (de tip int), sex(de tip String), cetatenie(de tip String),
judet(de tip String), oras(de tip String), strada(de tip String), diagnostic(de
tip String), medicamente(de tip String), compensata(de tip Boolean),
data(de tip Date) nu prezintă nici o adnotare Column, iar numele
câmpului din baza va fi automat acelaşi cu numele proprietăţii din obiect
RetetaAnimal.java – este o clasă model ce descrie câmpurile unei reţete prescrise de
medicul veterinar:
a) idRetetaAnimal – este de tip Long şi prezintă adnotarea PK
b) idMedic – este de tip Long şi prezintă adnotarea Column; el face legătura
cu obiectul Medic
c) numePosesor – este de tip String şi prezintă adnotarea Column
d) nrTelefon – este de tip String şi prezintă adnotarea Column
e) tipAnimal –este de tip String şi prezintă adnotarea Column
f) varstaAnimal –este de tip int şi prezintă adnotarea Column
g) câmpurile tratament(de tip String), diagnostic(de tip String),
comentariu(de tip String), data(de tip Date) nu au adnotare, numele
câmpului din tabel fiind acelaşi cu numele proprietăţii din obiect
FormularMedicinaNaturista.java –este o clasă model ce descrie un formular pe care ar
putea să îl elibereze medicul naturist şi care conţine câmpurile:
a) idFormular – este de tip Long şi prezintă adnotarea PK
b) idMedic – este de tip Long şi prezintă adnotarea Column; el face legătura
cu obiectul Medic
c) numePacient – este de tip String şi prezintă adnotarea Column
d) problemeAnterioare –este de tip String şi prezintă adnotarea Column
27
e) câmpurile varsta(de tip int), diagnostic(de tip String), remediu(de tip
String) ,data(de tip Date) nu au o adnotare, numele câmpului asociat din
tabel fiind acelaşi cu numele proprietăţii
Programare.java – este o clasă model ce descrie câmpurile unui formular de realizare a
unei programări la medic şi conţine cîmpurile:
a) idProgramare – este de tip Long şi prezintă adnotarea PK
b) numeMedic – este de tip String şi prezintă adnotarea Column; el face
legătura cu obiectul Medic
c) numePacient – este de tip String şi prezintă adnotarea Column
d) dataProgramare –este de tip Date şi prezintă adnotarea Column
e) oraProgramare –este de tip int şi prezintă adnotarea Column
Pentru a ne folosi de aceste obiecte şi a le pune la lucru am creat pachetele
org.licenta.aplicatie.medic, org.licenta.aplicatie.farmacist, org.licenta.aplicatie.pacient care
fac apel la aceste modele prin intermediul câmpurilor din interfeţele grafice.
Pentru lansarea proiectului este suficientă rularea clasei Proiect.java din pachetul
org.licenta.proiect care apelează prin elementele de interfaţă clasele corespunzătoare celor trei
pachete descrise anterior.
4.4. Funcţionalităţile sistemului și tratarea problemelor
Prima şi una din cele mai importante caracteristici ale sistemului ORM trebuie să fie
independenţa faţă de baza de date. Programatorul trebuie să poată să folosească ORM-ul în
acelaşi mod indiferent de sistemul de management al bazei de date folosit. Pentru realizarea
acestui lucru am ales să folosesc un fişier de configurare în care programatorul introduce
datele de logare în acelaşi mod indiferent de sistemul de management al bazei de date folosit.
Datele ce trebuie introduse în fişier sunt:numele driver-ului, url-ul bazei de date, numele bazei
de date, numele de utilizator şi parola. În continuare veţi vedea cum trebuie să arate conţinutul
unui astfel de fişier.
Secvenţa 4.1.- Date pentru conexiunea la bază
După introducerea datelor acestea vor fi preluate şi folosite pentru formarea
parametrilor necesari conectării. Formarea parametrilor este o parte foarte importantă
deoarece aceştia nu sunt la fel pentru toate sistemele de management ale bazelor de date
relaţionale. Formarea acestora diferă în special pentru Microsoft SQL Server.
1. driver = org.postgresql.Driver 2. url = jdbc:postgresql://localhost:5432/ 3. databaseName = aplicatie 4. userName = alin 5. userPassword = iomanigar
28
Secvenţa 4.2. – Formarea parametrilor şi stabilirea conexiunii
După cum se poate observa în cazul Microsoft conectarea se face folosind un singur
parametru ce conţine toate datele de logare pe când la restul SGBD-urilor conectarea se face
folosind trei parametri. În cazul în care conexiunea nu s-a făcut cu succes se va primi un
mesaj corespunzător.
O altă parte importantă sistemului este implementarea celor patru operaţii:
read(object), readAll(bbject), delete(object), save(object).
Metoda read(Object) trebuie să citească toate câmpurile obiectului specificat din care
iniţial se cunoaşte doar id-ul. Această metodă, pe lângă studierea clasei cu ajutorul API-ului
de Reflection, se foloseşte de metoda read(Long) din clasa QueryActions ce returnează un
HashMap(conţine înregistrari de forma cheie=valoare) în care cheia este numele coloanei din
tabel şi valoarea este valoarea ce o are înregistrarea căutată în tabel. Pentru implementarea
acestei metode am folosit API-ul de JDBC.
Clasa QueryActions are ca şi câmp o variabila de tip String căreia i se asignează o
valoare la instanţierea unui obiect de tip QueryActions prin constructor.
Pentru maparea clasei pe tabel am folosit o adnotare denumită @Table. Aceasta
primește ca parametru un String ce reprezintă numele tabelului. O parte foarte importantă a
implementării este reprezentată de studierea adnotărilor. În rândurile următoare voi
exemplifica și explica modul de studiu al adnotărilor.
Secvenţa 4.3. – Extragerea unei adnotări prezentă pe clasă şi studierea parametrului acesteia
1. try { 2. 3. if(driverName.equals("com.microsoft.sqlserver.jdbc.SQLServerDriver")){ 4. String con =url+";databaseName="+databaseName+";user="+usrName+ 5. ";password="+usrPass; 6. connection = DriverManager.getConnection(con); 7. } else { 8. connection = DriverManager.getConnection(url+databaseName,usrName, 9. usrPass); 10. } 11. } catch (SQLException e) { 12. System.out.println("Conectarea nu a reusit!"); 13. }
1. public <T> String getTableName(T object) { 2. 3. Class objectClass = object.getClass(); 4. Annotation annotation =objectClass.getAnnotation(Table.class); 5. Table table = (Table) annotation; 6. String tableName = table.name(); 7. 8. return tableName; 9. }
29
În liniile de cod de mai sus se află o metodă ce studiază dacă asupra clasei se afla
adnotări. La linia patru se poate observa că se extrag doar adnotările de tip Table iar la linia
șase se extrage numele tabelului ce va fi folosit mai departe.
O altă metodă folosită în metoda read este metoda getId. Aceasta extrage pe baza
adnotării @PK id-ul obiectului. Adnotarea @PK este folosită pentru a mapa cheia un câmp
din clasă pe cheia primară din tabel. În rândurile următoare se poate vedea modalitatea de
extragere a valorii id-ului.
Secvenţa 4.4. – Extragerea valorii id-ului
În liniile de mai sus se verifică două condiții necesare pentru a stabili dacă un câmp
este mapat corect pe cheia primară din tabel și anume să dispună de o adnotare de tip @PK si
să fie de tipul Long. Verificarea tipului este necesară deoarece tabele pot ajunge la dimensiuni
foarte mari, iar tipul Long oferă o gamă de valori suficient de mare pentru a permite un număr
mare de înregistrări.
În metoda read(object) primi paşi sunt reprezentaţi de folosirea celor două metode
mai sus menţionate pentru a afla numele tabelului pe care este mapată clasa şi id-ul obiectului.
Numele tabelului va fi folosit pentru a instanţia un obiect de tip QueryActions iar id-ul pentru
a extrage din baza de date înregistrarea cu id-ul dat ca parametru. Asignarea de valori
câmpurilor obiectului se face pe baza cheii din HashMap-ul rezultat în urma aplicării metodei
read(Long).
Maparea Câmpurilor din obiect pe coloanele tabelului se face cu ajutorul adnotării
@Column ce primește ca parametru numele coloanei.Maparea se poate face și pe baza
numelui câmpului dacă numele acestuia este același cu numele coloanei din baza de date. Mai
jos puteți vedea liniile de cod prin care se face maparea efectivă.
1. if (annot instanceof PK 2. && field.getType().getSimpleName().equals("Long")) 3. { 4. try { 5. id = (Long) field.get(object); 6. } catch (IllegalArgumentException e) { 7. e.printStackTrace(); 8. } catch (IllegalAccessException e) { 9. e.printStackTrace(); 10. } 11. 12. } 13. } 14. 15. return id;
30
Secvenţa 4.5. – Studul adnotării Column pentru maparea unui câmp
Variabila annots din codul de mai sus reprezintă vectorul ce conține adnotările unui
câmp.
Adnotarea @NotPersistent ce apare în cod este folosită pentru câmpurile ce nu vor fi
folosite pentru persistența în baza de date.
Următoarea metodă implementată este readAll(object). Spre deosebire de metoda
anterioară, aceasta nu folosește metoda getId deoarece scopul ei este să citească toata obiecte
de tipul celui specificat, și nu unul anume. O metodă nouă folosită in dezvoltarea acesteia este
metoda getColumns din clasa QueryActions.
Metoda getColumns returnează o listă de String-uri ce reprezintă numele coloanelor
exact în ordinea în care acestea se găsesc în tabel. Această metodă este utilă deoarece pe baza
rezultatului returnat vom putea asigna valori câmpurilor din obiecte indiferent de ordinea în
care acestea sunt declarate. Astfel programatorul nu va fi obligat să știe exact ordinea
coloanelor din baza de date. O altă utilitate a acestei metode este și faptul că putem extrage
din lista rezultată numele coloanei ce conține id-ul, acest lucru fiind necesar deoarece
maparea câmpului pentru id se face doar cu adnotarea @PK, nespecificând numele coloanei
cu id-ul nicăieri în clasa noastră. În metoda readAll(object) am folosit această metodă pentru
cea de-a doua utilitate. Metoda getColumns a fost implementată cu ajutorul JDBC.
Secvenţa 4.6. – Obţinerea valorii id-ului
În codul de mai sus este exemplificată modalitatea de extragere a coloanei ce conține
id-ul.
1. if (annots.length == 0) { 2. colName = field.getName(); 3. } else { 4. 5. for (Annotation a : annots) { 6. if (a instanceof Column) { 7. 8. Column col = (Column) a; 9. colName = col.name(); 10. } else if (!(a instanceof NotPersistent)) { 11. 12. colName = field.getName(); 13. } 14. } 15. }
1. try { 2. Object firstKey = columns.get(0); 3. field.set(inst, help.get(i).get(firstKey)); 4. } catch (IllegalArgumentException|IllegalAccessException e) { 5. e.printStackTrace(); 6. }
31
Acestă metodă returnează o listă cu obiectele de tipul celui specificat găsite în baza
de date. Aceste lucru se realizează cu ajutorul Reflection folosind metoda newInstance() pusă
la dispoiție de API-ul e Reflection. Pentru fiecare HashMap din lista de HashMap-uri
returnată de metoda readAll() din QueryActions se creează o noua instanță a obiectului
specificat.
Secvenţa 4.7. –Instanţierea unui obiect folosint Reflection
În afară de câmpul ce se mapează pe coloana ce conține id-ul restul coloanelor se
mapează similar cu cele de la metoda read(object).
Cea de-a treia metodă este cea care ne ajută la ștergerea unui obiect de tipul celui dat
ca parametru din baza de date, și anume metoda delete(object).
În această metodă am folosit două metode amintite mai devreme, getId() și
getTableName(), si o a treia metodă din clasa QueryActions.Această metodă din clasa
QueryActions se numește delete și primește ca parametru o variabilă de tip
Long(delete(Long)). Având în vedere că este o metodă de dimensiune mică voi pune mai jos
întreaga metodă.
Secvenţa 4.8. –Metoda delete(object)
Ultima metodă ce trebuie implementată este metoda save(object). Această metodă
trebuie fie să insereze o nouă înregistrare în baza de date dacă id-ul obiectului dat ca
parametru este null, fie să facă un update asupra obiectului din baza de date dacă id-ul este o
valoare ce există în baza de date.
Această metodă folosește metoda getId() pentru a afla id-ul obiectului dat ca
parametru, getTableName() pentru a afla numele tabelului în care se va face salvarea,
getColumns din clasa QueryActions. Aici metoda getColumns() este folosită petru a studia
ordinea coloanelor din tabel. Și nu în ultimul rând metoda save(Long, List<Object>) din clasa
QueryActions. Această metodată primește ca parametri id-ul înregistrării şi lista de valori pe
care să le insereze sau să le modifice în baza de date.
1. for (int i = 0; i < help.size(); i++) { 2. try { 3. inst = object.getClass().newInstance(); 4. } catch (InstantiationException | IllegalAccessException e) { 5. e.printStackTrace(); 6. }
...
..
1. public <T> void delete(T object) { 2. 3. Long id = getId(object); 4. 5. String tableName = getTableName(object); 6. 7. QueryActions action = new QueryActions(tableName); 9. action.delete(id); 10. }
32
Secvenţa 4.9. – Studiul ordinii coloanelor
În liniile de cod de mai sus se află un exemplu de aflare a poziției unei coloane în
tabel. Variabila pos reprezintă poziția pe care va fi pusă valoarea câmpului obiectului în lista
de valori ce va fi trimisă ca parametru metodei save din QueryActions. În metoda save(object)
formăm din câmpurile obiectului dat ca parametru lista de valori necesară metodei save(Long,
List< Object >).
În rândurile următoare voi vorbi despre metoda save(Long, List< Object >) din clasa
QueryActions. În această metodă se folosesc alte două metode implementate în aceeași clasă:
update(Long, List< Object >) și metoda insert(List< Object >).
Sevenţa 4.10. – Metoda save(Long, List)
În aceste două metode partea cea mai dificilă și cea mai importantă a fost formarea
interogării. O atenție specială au necesitat obiectele de tip String și cele de tip Date.
Obiectele de tip String au valori ce sunt încadrate între ghilimele și nu toate sistemele
de gestiune a bazelor de date relaționale acceptă valori încadrate între ghilimele, însă absolut
toate acceptă încadrarea între apostrof. În liniile de cod următoare se va vedea cum am
înlocuit ghilimelele cu apostrof în cazul metodei update și cum am format interogarea, aceasta
trebuind să se formeze corect indiferent de numărul de elemente ale listei dată ca parametru.
Secvenţa 4.11. – Tratarea obiectelor de tip String şi Date
1. if (a instanceof Column) { 2. Column col = (Column) a; 3. colName = col.name(); 4. pos = coloane.indexOf(colName) - 1; 5. }
...
1. public void save(Long id, List<Object> valori) { 2. 3. if (id != null) 4. update(id, valori); 5. else 6. insert(valori); 7. }
1. if (valori.get(i).getClass().getName().equals("java.lang.String")) { 2. help += coloane.get(i) + "=" + "\'" + valori.get(i)+ "\', "; 3. } else if (valori.get(i).getClass().getName().equals("java.util.Date")) { 4. java.sql.Date data = new Date(((java.util.Date) valori.get(i)).getTime()); 5. help += coloane.get(i) + "=" + "\'" + data + "\', "; 6. } else { 7. help += coloane.get(i) + "=" + valori.get(i) + ", "; 8. }
33
După cum se poate observa obiectele de tip Date trebuie convertite la un alt tip de
obiect Date care să fie compatibil cu cel din baza de date. De asemenea și valorile unor
asemenea obiecte trebuie încadrate cu apostrof. Diferența la metoda insert era că nu se adaugă
șiruri de caractere de forma nume_coloană=valoare, ci după valoare se pune caracterul
virgulă.
În ambele metode mai trebuie făcută o verificare, și anume dacă valoarea se află pe
ultima poziție în lista de valori, formarea interogării fiind puțin diferită.
Secvenţa 4.12.- Tratarea ultimei poziţii la update
Secvenţa 4.13. – Tratarea ultimei poziţii la insert
Secvențele de cod de mai sus ilustrează modul în care se formează interogarea
pentru ultima valoare din listă. Dupa cum se poate observa în prima secvență(extrasă din
metoda update) după ultima valoare urmează caluză WHERE pe când în cazul metodei insert
, ilustrată în cea de-a doua secvență de cod, după ultima valoare urmează închiderea unei
paranteze.
În cazul metodelor din clasa ORMHandler, și anume read(object), readAll(object),
delete(object), save(object), am implementat astfel încât acestea să accepte orice tip de obiect.
Acest lucru a fost realizat făcând ca aceste patru metode să fie generice, astfel ele putând
accepta orice tip de obiect.
1.if(valori.get(i).getClass().getName().equals("java.lang.String"2. )){ 3. help += coloane.get(i) + "=" + "\'" + valori.get(i) 4. + "\' WHERE " + idTabel + "=" + id; 5. } else if (valori.get(i).getClass().getName() 6. .equals("java.util.Date")){ 7. java.sql.Date data = new 8. Date(((java.util.Date)valori.get(i)).getTime()); 9. help += coloane.get(i) + "=" + "\'" + data + "\' WHERE " 10. + idTabel + "=" +id; 11. 12. }
1. if(lista.get(i).getClass().getName().equals("java.lang.String" 2. )) { 3. help += "\'" + lista.get(i) + "\') "; 4. } else if 5. (lista.get(i).getClass().getName().equals("java.util.Date")) { 6. java.sql.Date data = new 7. Date(((java.util.Date)lista.get(i)).getTime()); 8. help += "\'" + data + "\') "; 9. }
34
Secvenţa 4.14. – Metode generice
Așa cum se poate observa în secvența de mai sus la toate cele patru metode
este prezent parametrul care devine tipul obiectului dat ca parametru. Acest lucru face ca
metodele să accepte ca parametru orice tip de obiect. Atunci când metodele sunt folosite nu
este nevoie de vreun apel special de metodă, se va apela pur și simplu prin numele metodei și
i se va da ca parametru orice tip de obiect se dorește. Mai jos voi ilustra apelul unei astfel de
metode.
Secvenţa 4.15. – Exemplu de apel metodă generică
După cum se poate observa faptul că metoda read(object) este una generică nu
complică deloc folosirea ei.
Prima problemă ce apare la maparea cheii primare este compatibilitatea numelui
câmpului sau cum putem indica cât mai simplu faptul că un câmp este corespondentul cheii
primare din tabel. În rezolvarea acestei probleme am plecat de la considerentul că pentru un
tabel prima coloană va fi tot timpul reprezentată de cheia primară.
Secvenţa 4.16. – Extragerea numelui coloanei cu id-ul
Mai sus se află una din secvenţele de cod ce rezolvă această problemă. Executarea
interogării din prima linie duce la reţinerea tuturor datelor despre tabel în variabila helpRs.
Extragerea metadatelor se face cu metoda getMetaData(). Metadatele reprezintă numele
coloanelor, tipurile acestora, numărul coloanelor etc. Metoda getColumnName(1) returnează
public <T> void save(T object)
public <T> List<T> readAll(T object)
public <T> void delete(T object)
public <T> void read(T object)
1. Serial serial = new Serial(); 2. serial.setId(2L); 3. ORMHandler h = new ORMHandler(); 4. h.read(serial);
1. String helpQuery = "SELECT * FROM " + this.table; 2. try { 3. 4. Statement helpStmt = connect.createStatement(); 5. ResultSet helpRs = helpStmt.executeQuery(helpQuery); 6. rsmd = helpRs.getMetaData(); 7. idTabel = rsmd.getColumnName(1); 8. 9. } catch (SQLException e) { 10. e.printStackTrace(); 11. System.out.println("Probleme la metadata!"); 12. }
35
numele primei coloane din tabel, adică cea care conţine cheia primară. Menţionez că metoda
getMetaData returneză coloanele exact în ordinea în care ele se găesc în tabel.
O altă metodă de rezolvare folosită în implementarea sistemului este reţinerea tuturor
numelor coloanelor într-o listă şi folosirea primului element din listă atunci când avem nevoie
de coloana pentru cheia primară. Acest lucru se realizeaza cu ajutorul metodei getColumns
din clasa QueryActions.
Secvenţa 4.17. – Formarea listei cu numele coloanelor
Adăugarea acestei secvenţe de cod la secvenţa de mai devreme duce la formarea unei
liste cu numele coloanelor.
Un exemplu de folosire a celei de-a doua metode poate fi observat în metoa readAll.
Mai jos se află secvenţa de cod ce ilustreaza folosirea celei de-a doua metode.
Secvenţa 4.18. – Exemplu de utilizare listă de nume coloane
În liniile de cod de mai sus columns reprezintă lista cu numele coloanelor dintr-un
tabel. Numele primei coloane este extras şi este folosit pentru a se extrage valoarea cheii
primare dintr-un HashMap ce conţine înregistrări de tip cheie=valoare, cheia fiind numele
coloanei.
Cea de-a doua problemă la maparea cheii primare este generarea id-ului. Această
problemă a fost rezolvată ţinând cont doar de valoarea maximă a id-ului la un moment de
timp. Nu s-a ţinut cont de valoarea id-ului din sesiunile anterioare.
1. try { 2. for (int i = 1; i < rsmd.getColumnCount() + 1; i++) 3. rez.add(rsmd.getColumnName(i)); 4.} catch (SQLException e) { 5. e.printStackTrace(); 6. }
1. try { 2. Object firstKey = columns.get(0); 3. 4. field.set(inst, help.get(i).get(firstKey) 5. } catch (IllegalArgumentException| IllegalAccessException e) { 6. e.printStackTrace() 7. } 8. }
36
Secvenţa 4.19. – Generarea id-ului
În liniile de mai sus se poate observa că este extrasă valoarea maximă a cheii primare
din tabel. La final valoarea maximă este incrementată, astfel asigurând unicitatea cheii
primare. Dacă în tabel nu există înregistrări nu reprezintă o problema deoarece valoarea este
iniţializată cu 0 şi este incrementată oricum la final, fie că a găsit valori sau nu în tabel.
Rezolvarea celei de-a doua probleme a fost mai facilă deoarece pentru maparea
oricărui tip de dată ce conţine caractere am folosit direct clasa String. În Hibernate atunci
când se preiau date din baza de date sunt salvate într-un array ce conţine date de tip char. Apoi
făcându-se cast-ul la String dacă este nevoie. De asemenea atunci când se salvează un şir de
caractere în baza de date acestea sunt convertite în array de char-uri si pentru acest lucru este
nevoia de specificarea lungimii dacă acesta depăşeşte 255.
O altă parte a acestei lucrări ce a rămas neatinsă sunt adnotările. Acestea reprezintă
metadate ce pot fi adăugate claselor, pachetelor, câmpurilor. Sunt caraterizate de prezenţa
caracterului „@” la început. Spre deosebire de comentarii ele sunt recunoscute de compilator
şi apar ca elemente şi in API-ul de Reflection. Aici am folosit adnotări pentru maparea
claselor şi câmpurilor pe tabele şi coloane. Ele pot conţine parametrii care de asemenea sunt
cunoscuţi şi pot fi utilizaţi în cercetarea claselor şi a câmpurilor. Pentru mapare am folosit
următoarele adnotări:
a) @Table – aceasta poate primește ca parametru numele tabelului e care se mapează
clasa. Implementarea adnotării poate fi observată ma jos.
Secvenţa 4.20. – Adnotarea Table
Adnotarea @Retention este folosită pentru a specifica momentul când să fie luată în
considerare adnotarea. Pentru toate adnotările implementate am ales să fie active la rulare
b) @PK – am folosit-o pentru a mapa cheia primară
1. Long idRow = 0L; 2. String helpQuery = "SELECT * FROM " + this.table; 3. try { 4. Statement helpStmt = connect.createStatement(); 5. resultSet = helpStmt.executeQuery(helpQuery); 6. while (resultSet.next()) { 7. Long idRow1 = resultSet.getLong(1); 8. if (idRow1 > idRow) 9. idRow = idRow1; 10. } 12. } catch (SQLException e) { 13. e.printStackTrace(); 14. System.out.println("Probleme la metadata!"); 15. } 16. idRow++;
@Retention(RetentionPolicy.RUNTIME) public @interface Table { String name(); }
37
Secvenţa 4.21. – Adnotarea PK
Această adnotare nu primește nici un parametru.
c) Adnotarea @Column – folosită pentru a mapa orice câmp pe o coloană în cazul în
care numele coloanei şi cel al câmpului nu coincid.
Secvenţa 4.22. – Adnotarea Column
Primește ca parametru numele coloanei pe care se mapează și opțional tipul de date
și lungimea. Al doilea parametru și al treilea pot fi folosiți mai mult în cazul în care ORM-ul
creează și tabele și trebuie specificate tipurile coloanelor și lungimea dacă este vorba de date
de tip VARCHAR.
d) Adnotarea @NotPersistent – este folosită pentru a marca faptul că un câmp nu se
persistă în bază, astfel că el nu va fi luat în considerare la operațiile sistemului cu baza de
date.
Secvenţa 4.23. – Adnotarea NotPersistent
e) Adnotarea @NotNull – folosită în eventualitatea în care ORM-ul va genera și
tabele pentru a specifica la crearea tabelului că o coloană nu poate lua valori nule.
Secvenţa 4.24. – Adnotarea NotNull
@Retention(RetentionPolicy.RUNTIME) public @interface PK { }
@Retention(RetentionPolicy.RUNTIME) public @interface Column { String name(); String type() default "";
int length() default 100; }
@Retention(RetentionPolicy.RUNTIME) public @interface NotPersistent { }
@Retention(RetentionPolicy.RUNTIME) public @interface NotNull { }
38
f) Adnotarea @FK – folosită la la crearea de tabele pentru a mapa cheia externă din
tabel. Primește ca parametru numele tabelului de care se leagă tabelul mapat de clasa în care
se află câmpul cu adnotarea @FK.
Secvenţa 4.25. – Adnotarea FK
5. Testarea aplicaţiei
Pentru testarea aplicaţiei am dezvoltat o mini-aplicaţie grafică prin intermediul căreia
incerc să demonstrez aplicabilitatea şi funcţionalitatea ORM-ului. În continuare voi prezenta
modul de folosire al aplicaţiei de testare:
5.1. Fereastra principală
Conţine câte un buton care deschide fiecare componentă a aplicaţiei: cea pentru
medici, cea pentru famacişti şi cea pentru pacienţi.
Fig. 5.1. – Fereastra principală
@Retention(RetentionPolicy.RUNTIME) public @interface FK { String name(); }
39
5.2. Fereastra Medic
La apăsarea butonului Medic se va intra într-o fereastră de unde se poate alege
categoria din care face parte medicul, aplicaţia este destinată medicilor de familie, medicilor
naturişti şi medicilor veterinari.
Fig. 5.2. – Fereastra de categorii
La apăsarea butonului Medicină de familie se va deschide o fereastră de unde
medicul are posibilitatea de a elibera o reţetă şi de a vizualiza programările făcute de pacienţii
săi.
Fig. 5.3. – Fereastra pentru medici de familie
40
Pentru eliberarea unei reţete medicul va completa câmpurile din fereastră şi va apăsa
butonul Salvează, astfel reţeta va fi salvată în baza de date. La apăsarea butonului Salvează se
apelează metoda save(Object) din ORM-ul implementat.
Secvenţa 5.1. – Salvarea unei reţete
Secvența de cod de mai sus ilustrează modul de preluare și prelucrare a datelor din
interfață și apoi salvarea lor în baza de date cu ajutorul metodei save.
La apăsarea butonului Programări medicului îi sunt afişaţi toţi pacienţii ce s-au
programat pentru o consultaţie la el. Pentru afişarea programărilor este folosită metoda
readAll() din ORM.
Mai jos este exemplificată folosirea metodei readAll și afișarea datelor preluate într-
un tabel.
1. Reteta r = new Reteta(); 2. 3. r.setIdMedic(f.getIdMedic(Long.valueOf(text4.getText())); 4. r.setJudet(text_1.getText()); 5. r.setOras(text_2.getText()); 6. r.setNumePacient(text1.getText()); 7. r.setStrada(text_3.getText()); 8. 9. r.setNrStrada(Long.valueOf(text_4.getText())); 10. r.setCetatenie(text_5.getText()); 11. r.setVarsta(Integer.parseInt(text_6.getText())); 12. r.setMedicamente(text.getText()); 13. r.setDiagnostic(text2.getText()); 14. 15. int an = data.getYear()-1900; 16. int luna = data.getMonth(); 17. int zi = data.getDay(); 18. Date date = new Date(an, luna, zi); 19. r.setData(date); 20. 21. if (button.getSelection() == true) { 22. r.setSex(button.getText()); 23. } else { 24. r.setSex(button_1.getText()); 25. } 26. r.setCompensata(btnCheck.getSelection()); 27. 28. ORMHandler h=new ORMHandler(); 29. h.save(r); 30. } 31. }
41
Secvenţa 5.2. – Exemplu de folosire readAll
Fig. 5.4. – Vizualizarea programărilor
1. ORMHandler h = new ORMHandler();
2. Programare p = new Programare();
3. Functionalitate f = new Functionalitate();
4.
5. List<Programare> pr = h.readAll(p);
6.
7. if (f.verificaMedic(Long.valueOf(codIdentif.getText())) == 1) {
8. for (int i = 0; i < pr.size(); i++) {
9. String nume = f.getNumeMedic(Long.valueOf(codIdentif.getText()));
10. if (pr.get(i).getNumeMedic().equals(nume)) {
11.
12. tableItem = new TableItem(table, SWT.NONE);
13. tableItem.setText(0, pr.get(i).getNumePacient());
14. tableItem.setText(1, ""+pr.get(i).getDataProgramare());
15. tableItem.setText(2, ""+pr.get(i).getOraProgramare());
16.
17. }
18. }
19. }
42
Înainte de salvarea unei reţete sau de vizualizarea programărilor un medic trebuie să
introducă un cod de indentificare , unic şi cunoscut numai de el.
La apăsarea butonului Medicină naturistă se va deschide o fereastră asemănătoare cu
cea de la Medicină de familie în care un medic naturist va putea să prescrie un remediu pentru
pacient şi va putea să vizualizeze programările. Funcţionalitatea este similară cu cea de la
Medicină de familie.
Fig. 5.5. – Medicină naturistă
La apăsarea butonului Medicină veterinară se va deschide o fereastră în care un
medic veterinar va putea să elibereze tratamente pentru animalele pe care le consultă. Aici
avem funcţionalitate de introducere în baza de date a tratamentului.
Fig. 5.6. – Medicină Veterinară
43
5.3. Fereastra Farmacist
La apăsarea butonului Farmacist din fereastra principală se va deschide o fereastră în
care farmacistul are posibilitatea de a vizualiza reţetele unui pacient şi de a le printa. Printarea
se face în 2 paşi: întâi farmacistul scoate reţeta din baza de date sub forma unui fişier pdf,
după aceea fişierul pdf poate fi printat. În cadrul acestei secţiuni a aplicaţiei s-au folosit
metodele readAll şi read(Object) implementate în ORM.
Secvenţa 5.3. – Exemplu de folosire metoda read
Mai sus este ilustrat un exemplu de folosire a metodei read din sistemul ORM în
această aplicație. Este folosită pentru a afla numele medicului în funcție de id-ul acestuia. Id-
ul este salvat ca informație a unei rețete în baza de date, dar este folosit pentru a putea afla
numele medicului.
Fig 5.7. – Farmacist
Long idMedic=reteta.getIdMedic(); m.setIdMedic(idMedic); handler.read(m); numeMedic = m.getNumeMedic();
44
5.4. Fereastra Pacient
La apăsarea butonului Pacient din fereastra principală se deschide o fereastră cu
ajutorul căreia un pacient îşi poate face o programare la un medic. În aceasta secţiune am
folosit din ORM-ul implementat metodele save(Object) atunci când se salvează programarea
în baza de date şi metodele readAll() şi read(Object) atunci când am verificat dacă
programarea este posibilă. Programarea este salvată în baza de date doar dacă ora din ziua
aleasă de pacient la medicul dorit de el nu este ocupată.
În cazul în care programarea nu este posibilă pacientul va primi un mesaj în care este
rugat să aleaga altă ora, în caz contrar acesta va primi un mesaj prin care va fi informat că
înregistrarea a avut loc cu succes.
Fig. 5.8. – Fereastra pentru
programare
Fig. 5.9. – Mesaj în cazul în care
programarea s-a făcut cu succes
45
În această testare am încercat să folosesc tabele de dimensiuni şi tipuri de date cât
mai variate pentru a demonstra funcţionalitatea ORM-ului şi avantajele acestuia. ORM-ul a
fost testat atât pe PostgreSQL cât şi pe Microsoft SQL Server 2012 pentru a demonstra
independenţa faţă de baza de date.
Cel mai mare tabel din baza de date este cel de rețete de la medicii de familie.În
imaginile următoare voi arăta că acestea sunt de tipuri cât mai variate și că de asemena nu este
necesar ca ordinea câmpurilor din clasa ce mapează tabelul să fie aceeași cu ordinea
coloanelor din tabel.
Fig. 5.11. – Coloanele tabelului rețete
Fig. 5.10. – Mesaj în cazul în care
programarea nu este posibilă
46
Fig. 5.12. – Clasa Reteta ce mapează tabelul retete
Imaginea de mai sus este de asemenea și un exemplu concret de cum se face maparea
unei clase pe un tabel. Se poate vedea că maparea se poate face atât cu adnotări cât si pe baza
numelui câmpului dacă acesta se potrivește cu numele coloanei.
De asemenea testarea s-a făcut și cu câmpul compensata adnotat cu @NotPersistent
iar programul a generat o eroare de tipul NullPointerException deoarece fiind adnotat cu
@NotPersistent era ignorat la rulare, astfel că atunci când se făcea un insert în baza de date
valoarea corespunzătoare câmpului era null și nu se putea face insert-ul.
Am ales testarea sistemului pe acest gen de aplicație deoarece este un subiect de
actualitate, dar el poate fi folosit în orice domeniu ce folosește o bază de date, deoarece face
operarea cu baza de date mult mai ușoară și poate duce la rezultate îmbunătățite în domeniul
în care este utilizat.
47
6. Concluzii și Dezvoltări ulterioare
6.1. Dezvoltări ulterioare
Deși în stadiul actual al lucrării sistemul îndeplinește cerințele întotdeauna este loc
de îmbunătățiri și adăugări. Printre viitoare funcții ale sistemului ORM se pot număra:
a) Posibilitatea de a crea tabele direct, prin simpla mapare a claselor, astfel ele nu ar
mai trebui create în baza de date de către programator
b) Posibilitatea căutării după un câmp dat ca parametru sau a unei liste de câmpuri
c) Menținerea unui istoric al operațiilor efectuate pe baza de date
d) Posibilitatea mapării unei chei primare compuse
e) Crearea unei metode care căreia programatorul să îi trimită ca parametru o
interogare de orice fel, iar aceasta să întoarcă rezultatul interogării
6.2. Concluzii
Un ORM este o soluție viabilă pentru programatori atunci când doresc să îmbine
programarea orientată-obiect cu lucrul cu baze date deoarece este un sistem scalabil, rapid și
ușor de folosit.
Un astfel de sistem poate duce la creare de aplicații stabile într-un timp mult mai
scurt. De asemenea duce la scrierea unui cod uşor de întreținut și depanat deoarece reduce
cantitatea de cod și este și ușor de înțeles.
Cea mai bună metodă de mapare a obiectelor pe tabele este folosirea adnotărilor
deoarece este o metodă mai intuitivă și care nu necesită crearea unor fișiere de configurare
mari și greu de urmărit.
Pentru dezvoltarea unui sistem de mapare a obiectelor pe tabelele dintr-o bază de
date este necesară folosirea atât a API-ului de JDBC cât și a API-ului de Reflection.
Acest ORM încearcă să trateze unele probleme a căror rezolvare ar duce la o utilizare
mai ușoară și la o funcționare mai aproape de ceea ce se așteaptă un programator.
Un astfel de sistem poate fi folosit la implementarea modelului și chiar a unei părți
din controller din pattern-ul de proiectare MVC(Model-View-Controller), care este una din
cele mai utilizate metode la dezvoltarea aplicațiilor atât desktop cât și web.
48
7. Bibliografie
[1] Tanasa Stefan, Andrei Stefan, Olaru Cristian, 2007. „Java de la 0 la Expert –
Ediția a II-a revizuită”. POLIROM, Iași
[2] Roland Barcia, Geoffrey Hambrick, Kyle Brown, Robert Peterson, Kulvir Singh
Bhoga. 2008. “Persistence in the Enterprise: A Guide to Persistence Technologies”. IBM
Press, Boston
[3]Jakob Jenkov. „Java Reflection Tutorial”. http://tutorials.jenkov.com/java-
reflection/index.html, accesat la data de 13.06.2013
[4]Jakob Jenkov. „Java PDF Generation with IText”.
http://tutorials.jenkov.com/java-itext/index.html, accesat la data de 23.06.2013
[5]Lars Vogel. 2010. „Creating PDF with Java and iText - Tutorial”.
http://www.vogella.com/articles/JavaPDF/article.html, accesat la date de 23.06.2013
[6]Oracle Java SE Documentation, “Annotations”.
http://docs.oracle.com/javase/6/docs/technotes/guides/language/annotations.html, accesat la
data de 28.05.2013
[8]Hibernate Documentation. 2013 „Hibernate Developer Guide”.
http://docs.jboss.org/hibernate/orm/4.3/devguide/en-US/html/, accesat la data de 30.05.2013
[9] Bitcell. 2009. „Tutoriale Java”. http://www.bitcell.info/tutorial-java-introducere-
despre-platforma-t674.html, accesat a data de 29.06.2013
[10]Eclipse. 2013. „SWT – The Standard Widget Toolkit”.
http://www.eclipse.org/swt/, accesat la data de 20.06.2013
[11] Oracle FAQ’s. 2013. „JDBC”. http://www.orafaq.com/wiki/JDBC, accesat la
data de 10.06.2013
[12]Wikipedia. 2013. „Object-relational mapping”.
https://en.wikipedia.org/wiki/Object-relational_mapping, accesat la data de 26.06.2013
[13]Tutorial Debug în mediul de dezvoltare Eclipse, 2007,
http://eclipsetutorial.sourceforge.net/debugger01/lesson01.html, accesat la data de 6.06.2013
[14] Documentaţie Oracle Interface ResultSetMetadata,
http://docs.oracle.com/javase/1.5.0/docs/api/java/sql/ResultSetMetaData.html, accesat la data
de 8.06.2013
[15] Historical perspective of ORM, Kenneth Downs,2010, http://database-
programmer.blogspot.ro/2010/12/historical-perspective-of-orm-and.html , accesat la data de
23.06.2013
[15] O-R Mapping, http://encyclopedia2.thefreedictionary.com/Object-
relational+mapping, accesat la data de 23.06.2013
[16] Wikipedia, 2013, PostgreSQL, http://ro.wikipedia.org/wiki/PostgreSQL,
accesat la data de 24.03.2013
49
[17] Top 5 best databases, Ramesh Natarajan, 2010,
http://www.thegeekstuff.com/2010/03/top-5-best-databases/ , accesat la data de 2.06.2013
[18] Wikipedia, 2013, Microsoft Sql Server,
http://ro.wikipedia.org/wiki/Microsoft_SQL_Server, accesat la data de 24.03.2013
[19] MSDN, 2012, Features Supported by the Editions of SQL Server 2012,
http://msdn.microsoft.com/en-us/library/cc645993(v=SQL.110).aspx, accesat la data de
24.03.2013
[20] Tutorial JVM, 2012, http://lunetistii.ro/forum/tutoriale-sofware/java-vm-virtual-
machine-t208815.html, accesat la 22.06.2013
[21] Lars Vogel 2010, Eclipse Shortcuts,
http://www.vogella.com/articles/EclipseShortcuts/article.html, accesat la data de 26.06.2013
[22]IBM, 2009, Java, JDBC and SQL Data Types,
http://publib.boulder.ibm.com/infocenter/db2luw/v8/index.jsp?topic=/com.ibm.db2.udb.doc/a
d/rjvjdata.htm, accesat la data de 12.06.2013
[23] Imagine, http://eofdreams.com/doctor.html , accesată la data de 18.06.2013
[24] Imagine farmacist, http://ataheri.wordpress.com/2012/10/26/c3-3-r3-reflect-in-
eportfolio-what-commitment-to-the-profession-means-personally/ , accesată la data de
18.06.2013
[25] Model reţetă naturistă, http://enoratip.ro/produse/imprimate-tipizate/retete-
medicale-bloc-3a4-100-file-offset/ , accesat la data de 18.06.2013
[26] Model reţetă medicală, http://www.gorjexclusiv.ro/exclusiv/spitalul-targu-jiu-
restant-la-retete-electronice.html , accesat la data de 18.06.2013
[27] Imagine siglă de sănătate,
http://www.infosapientia.ro/Evenimente/tabid/71/articleType/ArticleView/articleId/1280/Ziua
-mondiala-a-bolnavului-Pentru-a-iesi-din-penumbra.aspx , accesat la data ded 18.06.2013
[28] Wikipedia, 2013, JDBC,
http://en.wikipedia.org/wiki/Java_Database_Connectivity, accesat la data de 10.06.2013
[29] Documentaţie Oracle, 2013, JDBC,
http://docs.oracle.com/javase/tutorial/jdbc/basics/connecting.html, accesat la data de
10.06.2013
[30] Tutorial generare PDF, http://www.openlogic.com/wazi/bid/188064/iText-
Generate-PDFs-in-Java, accesat la data de 23.06.2013
50
ANEXĂ COD
1) DataConn.java
package org.licenta.miniorm.connection;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class DataConn {
public String driverName;
public String url;
public String databaseName;
public String usrName;
public String usrPass;
private Properties getInfo() {
InputStream is = getClass().getResourceAsStream( "connection_data.properties");
Properties props = new Properties();
try {
props.load(is);
} catch (IOException e1) {
e1.printStackTrace();
}
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
return props;
}
public DataConn() {
Properties props = getInfo();
this.databaseName = props.getProperty("databaseName");
this.driverName = props.getProperty("driver");
51
this.url = props.getProperty("url");
this.usrName = props.getProperty("userName");
this.usrPass = props.getProperty("userPassword");
}
}
2) ConnectionDB.java
package org.licenta.miniorm.connection;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class ConnectionDB {
public DataConn data = new DataConn();
public String driverName = data.driverName;
public String url = data.url;
public String databaseName = data.databaseName;
public String usrName = data.usrName;
public String usrPass = data.usrPass;
public Connection connect() {
Connection connection = null;
System.out.println(this.driverName);
try {
Class.forName(driverName);
} catch (ClassNotFoundException e1) {
System.out.println("Problema la incarcarea driver-ului!");
}
try {
if(driverName.equals("com.microsoft.sqlserver.jdbc.SQLServerDriver")){
String con =url+";databaseName="+databaseName+";user="+usrName+";password="+usrPass;
connection = DriverManager.getConnection(con);
} else {
connection = DriverManager.getConnection(url+databaseName,usrName,usrPass);
}
52
} catch (SQLException e) {
System.out.println("Conectarea nu a reusit!");
}
System.out.println("Conectarea s-a facut cu succes!");
return connection;
}
}
3)QueryActions.java
package org.licenta.miniorm.queryactions;
import java.sql.Connection;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.licenta.miniorm.connection.ConnectionDB;
/**
* @author ALIN
* Clasa pentru operatiile de lucru cu baza de date.
*/
public class QueryActions {
private String table;
public QueryActions(String table) {
this.table = table;
}
public List<String> getColumns() {
List<String> rez = new ArrayList<String>();
ConnectionDB conn = new ConnectionDB();
Connection connect = conn.connect();
ResultSetMetaData rsmd = null;
String helpQuery = "SELECT * FROM " + this.table;
53
try {
Statement helpStmt = connect.createStatement();
ResultSet helpRs = helpStmt.executeQuery(helpQuery);
rsmd = helpRs.getMetaData();
} catch (SQLException e) {
e.printStackTrace();
System.out.println("Probleme la metadata!");
}
try {
for (int i = 1; i < rsmd.getColumnCount() + 1; i++)
rez.add(rsmd.getColumnName(i));
} catch (SQLException e) {
e.printStackTrace();
}
return rez;
}
/**
* Intoarce din baza inregistrarea cu id-ul id
* @param id
* @return
*/
public HashMap<String, Object> read(Long id) {
HashMap<String, Object> rezultat = new HashMap<String, Object>();
ConnectionDB conn = new ConnectionDB();
Connection connect = conn.connect();
String idTabel = "";
ResultSet resultSet = null;
ResultSetMetaData rsmd = null;
String helpQuery = "SELECT * FROM " + this.table;
try {
Statement helpStmt = connect.createStatement();
ResultSet helpRs = helpStmt.executeQuery(helpQuery);
rsmd = helpRs.getMetaData();
idTabel = rsmd.getColumnName(1);
54
} catch (SQLException e) {
e.printStackTrace();
System.out.println("Probleme la metadata!");
}
String query = "SELECT * FROM " + this.table + " WHERE " + idTabel
+ "=" + id;
try {
Statement statement = connect.createStatement();
resultSet = statement.executeQuery(query);
} catch (SQLException e) {
e.printStackTrace();
System.out.println("Probleme cu query-ul principal!");
}
try {
Integer count = rsmd.getColumnCount();
while (resultSet.next()) {
for (int i = 1; i < count + 1; i++) {
String cheie = rsmd.getColumnName(i);
Object valoare = resultSet.getObject(rsmd.getColumnName(i));
rezultat.put(cheie, valoare);
}
}
} catch (SQLException e1) {
e1.printStackTrace();
System.out.println("Probleme la crearea hashmap-ului!");
}
try {
connect.close();
} catch (SQLException e) {
System.out.println("Problema la inchiderea conexiunii la baza!");
}
return rezultat;
}
/**
55
* Intoarce din baza toate inregistrarile pentru tabel
*
* @return
*/
public List<HashMap<String, Object>> readAll() {
List<HashMap<String, Object>> rezultat = new ArrayList<HashMap<String, Object>>();
ConnectionDB conn = new ConnectionDB();
Connection connect = conn.connect();
ResultSet resultSet = null;
ResultSetMetaData rsmd = null;
String helpQuery = "SELECT * FROM " + this.table;
try {
Statement helpStmt = connect.createStatement();
resultSet = helpStmt.executeQuery(helpQuery);
rsmd = resultSet.getMetaData();
} catch (SQLException e) {
e.printStackTrace();
System.out.println("Probleme la metadata!");
}
try {
Integer count = rsmd.getColumnCount();
while (resultSet.next()) {
HashMap<String, Object> hash = new HashMap<String, Object>();
for (int i = 1; i < count + 1; i++) {
hash.put(rsmd.getColumnName(i),
resultSet.getObject(rsmd.getColumnName(i)));
}
rezultat.add(hash);
}
} catch (SQLException e1) {
e1.printStackTrace();
System.out.println("Probleme la crearea hashmap-ului!");
}
try {
connect.close();
} catch (SQLException e) {
System.out.println("Problema la inchiderea conexiunii la baza!");
56
}
return rezultat;
}
/**
* Sterge din tabel inregistrarea cu id-ul id
*
* @param id
*/
public void delete(Long id) {
ConnectionDB conn = new ConnectionDB();
Connection connect = conn.connect();
String idTabel = "";
ResultSetMetaData rsmd = null;
Statement stmt = null;
String helpQuery = "SELECT * FROM " + this.table;
try {
Statement helpStmt = connect.createStatement();
ResultSet helpRs = helpStmt.executeQuery(helpQuery);
rsmd = helpRs.getMetaData();
idTabel = rsmd.getColumnName(1);
} catch (SQLException e) {
e.printStackTrace();
System.out.println("Probleme la metadata!");
}
String query = "DELETE FROM " + this.table + " WHERE " + idTabel + "="
+ id;
try {
stmt = connect.createStatement();
stmt.executeUpdate(query);
} catch (SQLException e) {
e.printStackTrace();
}
try {
connect.close();
} catch (SQLException e) {
System.out.println("Problema la inchiderea conexiunii la baza!");
57
}
}
/**
* Face o inserare in baza cu valorile din lista
*
* @param lista
*/
public void insert(List<Object> lista) {
ConnectionDB conn = new ConnectionDB();
Connection connect = conn.connect();
String help = "(";
Statement stmt = null;
ResultSet resultSet = null;
Long idRow = 0L;
String helpQuery = "SELECT * FROM " + this.table;
try {
Statement helpStmt = connect.createStatement();
resultSet = helpStmt.executeQuery(helpQuery);
while (resultSet.next()) {
Long idRow1 = resultSet.getLong(1);
if (idRow1 > idRow)
idRow = idRow1;
}
} catch (SQLException e) {
e.printStackTrace();
System.out.println("Probleme la metadata!");
}
idRow++;
help += idRow + ", ";
for (int i = 0; i < lista.size(); i++) {
if (i < lista.size() - 1) {
if (lista.get(i).getClass().getName()
.equals("java.lang.String")) {
help += "\'" + lista.get(i) + "\', ";
} else if (lista.get(i).getClass().getName()
.equals("java.util.Date")) {
58
java.sql.Date data = new Date(
((java.util.Date) lista.get(i)).getTime());
help += "\'" + data + "\', ";
} else {
help += lista.get(i) + ", ";
}
} else {
if (lista.get(i).getClass().getName()
.equals("java.lang.String")) {
help += "\'" + lista.get(i) + "\') ";
} else if (lista.get(i).getClass().getName()
.equals("java.util.Date")) {
java.sql.Date data = new Date(
((java.util.Date) lista.get(i)).getTime());
help += "\'" + data + "\') ";
} else {
help += lista.get(i) + ")";
}
}
}
String query = "INSERT INTO " + this.table + " VALUES " + help;
try {
stmt = connect.createStatement();
stmt.executeUpdate(query);
} catch (SQLException e) {
e.printStackTrace();
}
try {
connect.close();
} catch (SQLException e) {
System.out.println("Problema la inchiderea conexiunii la baza!");
}
}
/**
*
59
* Updateaza in tabel cu lista de valori data, inregistrarea cu id-ul id
*
* @param id
* @param valori
*/
public void update(Long id, List<Object> valori) {
ConnectionDB conn = new ConnectionDB();
Connection connect = conn.connect();
String idTabel = "";
ResultSetMetaData rsmd = null;
String help = "UPDATE " + this.table + " SET ";
List<String> coloane = new ArrayList<String>();
String helpQuery = "SELECT * FROM " + this.table;
try {
Statement helpStmt = connect.createStatement();
ResultSet helpRs = helpStmt.executeQuery(helpQuery);
rsmd = helpRs.getMetaData();
idTabel = rsmd.getColumnName(1);
for (int i = 2; i < rsmd.getColumnCount() + 1; i++) {
coloane.add(rsmd.getColumnName(i));
}
} catch (SQLException e) {
e.printStackTrace();
System.out.println("Probleme la metadata!");
}
for (int i = 0; i < valori.size(); i++) {
if (i < valori.size() - 1) {
if (valori.get(i).getClass().getName()
.equals("java.lang.String")) {
help += coloane.get(i) + "=" + "\'" + valori.get(i)
+ "\', ";
} else if (valori.get(i).getClass().getName()
.equals("java.util.Date")) {
java.sql.Date data = new Date(
((java.util.Date) valori.get(i)).getTime());
help += coloane.get(i) + "=" + "\'" + data + "\', ";
} else {
60
help += coloane.get(i) + "=" + valori.get(i) + ", ";
}
} else {
if (valori.get(i).getClass().getName()
.equals("java.lang.String")) {
help += coloane.get(i) + "=" + "\'" + valori.get(i)
+ "\' WHERE " + idTabel + "=" + id;
} else if (valori.get(i).getClass().getName()
.equals("java.util.Date")) {
java.sql.Date data = new Date(
((java.util.Date) valori.get(i)).getTime());
help += coloane.get(i) + "=" + "\'" + data + "\' WHERE "
+ idTabel + "=" + id;
} else {
help += coloane.get(i) + "=" + valori.get(i) + " WHERE "
+ idTabel + "=" + id;
}
}
}
try {
Statement stmt = connect.createStatement();
stmt.executeUpdate(help);
} catch (SQLException e) {
e.printStackTrace();
}
try {
connect.close();
} catch (SQLException e) {
System.out.println("Problema la inchiderea conexiunii la baza!");
}
}
/**
* Insereaza in baza lista de valori daca id-ul este null, iar in caz
* contrar updateaza in tabel inregistrarea cu id-ul id
*
* @param id
61
* @param valori
*/
public void save(Long id, List<Object> valori) {
if (id != null)
update(id, valori);
else
insert(valori);
}
}
3) ORMHandler.java
/**
*
*/
package org.licenta.miniorm.ormhandler;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.licenta.miniorm.annotations.Column;
import org.licenta.miniorm.annotations.NotPersistent;
import org.licenta.miniorm.annotations.PK;
import org.licenta.miniorm.annotations.Table;
import org.licenta.miniorm.queryactions.QueryActions;
/**
* @author ALIN
*/
public class ORMHandler {
public ORMHandler() {
super();
}
@SuppressWarnings({ "rawtypes" })
62
public <T> Long getId(T object) {
Long id = null;
Class objectClass = object.getClass();
Field[] fields = objectClass.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
Annotation annot = field.getAnnotation(PK.class);
if (annot instanceof PK
&& field.getType().getSimpleName().equals("Long")) {
try {
id = (Long) field.get(object);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return id;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public <T> String getTableName(T object) {
Class objectClass = object.getClass();
Annotation annotation = objectClass.getAnnotation(Table.class);
Table table = (Table) annotation;
String tableName = table.name();
return tableName;
}
@SuppressWarnings({ "rawtypes" })
public <T> void read(T object) {
String colName = "";
Long id = getId(object);
String tableName = getTableName(object);
Class objectClass = object.getClass();
Field[] fields = objectClass.getDeclaredFields();
63
QueryActions action = new QueryActions(tableName);
HashMap<String, Object> rez = action.read(id);
for (Field field : fields) {
field.setAccessible(true);
Annotation[] annots = field.getAnnotations();
if (annots.length == 0) {
colName = field.getName();
} else {
for (Annotation a : annots) {
if (a instanceof Column) {
Column col = (Column) a;
colName = col.name();
} else if (!(a instanceof NotPersistent)) {
colName = field.getName();
}
}
}
if (rez.containsKey(colName)) {
try {
field.set(object, rez.get(colName));
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public <T> List<T> readAll(T object) {
String colName = "";
Object inst = null;
List<T> rez = new ArrayList<T>();
List<HashMap<String, Object>> help = new ArrayList<HashMap<String, Object>>();
Class objectClass = object.getClass();
Annotation annotation = objectClass.getAnnotation(Table.class);
Table table = (Table) annotation;
String tableName = table.name();
64
QueryActions action = new QueryActions(tableName);
List<String> columns = action.getColumns();
help = action.readAll();
for (int i = 0; i < help.size(); i++) {
try {
inst = object.getClass().newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
Class objectClass2 = inst.getClass();
Field[] fields2 = objectClass2.getDeclaredFields();
for (Field field : fields2) {
field.setAccessible(true);
Annotation[] annots = field.getAnnotations();
if (annots.length == 0) {
colName = field.getName();
System.out.println(colName);
} else {
for (Annotation a : annots) {
if (a instanceof PK
&& field.getType().getSimpleName()
.equals("Long")) {
try {
Object firstKey = columns.get(0);
field.set(inst, help.get(i).get(firstKey));
} catch (IllegalArgumentException
| IllegalAccessException e) {
e.printStackTrace();
}
}
if (a instanceof Column) {
Column col = (Column) a;
colName = col.name();
} else if (!(a instanceof NotPersistent)) {
colName = field.getName();
}
}
65
}
if (help.get(i).containsKey(colName)) {
try {
field.set(inst, help.get(i).get(colName));
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
rez.add((T) inst);
}
return rez;
}
public <T> void delete(T object) {
Long id = getId(object);
String tableName = getTableName(object);
QueryActions action = new QueryActions(tableName);
action.delete(id);
}
@SuppressWarnings("rawtypes")
public <T> void save(T object) {
Long id = getId(object);
String tableName = getTableName(object);
List<Object> valori = new ArrayList<Object>();
Class objectClass = object.getClass();
String colName = "";
int pos = 0;
QueryActions action = new QueryActions(tableName);
List<String> coloane = action.getColumns();
System.out.println(coloane);
for (int i = 0; i < coloane.size() - 1; i++) {
valori.add(null);
}
Field[] fields = objectClass.getDeclaredFields();
66
for (Field field : fields) {
field.setAccessible(true);
Annotation[] annots = field.getAnnotations();
if (annots.length == 0) {
colName = field.getName();
pos = coloane.indexOf(colName) - 1;
} else {
for (Annotation a : annots) {
if (a instanceof Column) {
Column col = (Column) a;
colName = col.name();
pos = coloane.indexOf(colName) - 1;
} else if (!(a instanceof NotPersistent)
&& !(a instanceof PK)) {
colName = field.getName();
pos = coloane.indexOf(colName) - 1;
} else {
colName = "";
}
}
}
if (colName != "") {
try {
valori.set(pos, (Object) field.get(object));
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
action.save(id, valori);
}
}