De-a lungul timpului modul în care erau stocate datele a creat numeroase probleme. În cazul dezvoltatorilor de aplicaţii, acestea privesc interacţiunea cu baza de date, partea de organizare a datelor la nivelul modulului de persistenţă fiind mai puţin importantă pentru un dezvoltator de soft (ex: schemele relaţionale, ierarhice, reţea, semantice etc).
La nivelul performanţei interacţiunii se puneau două probleme importante: scăderea timpului de răspuns şi creşterea gradului de încredere în datele conţinute în baza de date. Rezolvarea acestora depindea de mulţi factori, de la tipul de bridge folosit pentru conexiune şi până la securitate sau la păstrarea integrităţii.
Pe partea de organizare a datelor, la nivelul modulului de persistenţă, modelul relaţional al bazelor de date s-a impus de-a lungul timpului ca modelul cel mai utilizat. Introducerea numeroaselor constrângeri teoretice ale acestui model (axiomele lui Armstrong http://en.wikipedia.org/wiki/Armstrong%27s_axioms) precum şi implementarea multora dintre aceste constrângeri au dus la creşterea încrederii, relativ la integritatea datelor.
Revenim la dezvoltatorii de aplicaţie. Ei lucrează la modulul aplicaţiei şi acesta trebuia să interacţioneze cu modulul de persistenţă. Au existat numeroase căi de realizare a interacţiunii. Patru tipuri de bridge-uri sunt cunoscute, ca posibilităţi de conectare la o bază de date: JDBC-ODBC Bridge, Native-API Driver, Network-Protocol Driver şi Java to Database Protocol. Fiecare nou tip aducea un plus în creşterea vitezei de răspuns, a numărului de conexiuni disponibile şi a securităţii. Nu dorim să insistăm asupra acestora, dar pentru cei interesaţi putem aprofunda subiectul.
Totuşi, oricât de bună era conexiunea existau şi alte provocări: aceasta trebuia creată, trebuiau făcute cereri prin conexiunea creată, iar serverul de baze de date trebuia să trimită un răspuns, dar şi multe altele. Unele dintre acestea s-au rezolvat. Spre exemplu, există pool-uri de conexiuni. Conexiunile nu se recrează ci se refolosesc.
La nivelul softului de aplicaţie o problemă majoră mult timp a fost, de asemenea, SQL injection. Aceasta a cauzat distrugeri însemnate sau replicări nedorite ale bazei de date.
Suntem acum pregătiţi să identificăm utilitatea cărţii "Java Persistence with JPA1" scrisă de dr. Daoqi Yang.
Cartea este o expunere de nivel mediu şi avansat a rolului Java Persistence API (JPA) în dezvoltarea aplicaţiilor standalone (cu acces distribuit sau nu - baza de date fiind locală) sau pur distribuite, ce interacţionează cu baze de date, organizate, în special, după modelul relaţional.
Cum am afirmat mai devreme JPA poate fi folosit şi de către dezvoltatorii de aplicaţii standalone, dar performanţa este cu adevărat evidenţiată pentru aplicaţii distribuite, ce folosesc servere de aplicaţie ca middleware. Este şi cazul dezbătut pe larg în această carte. Aspectele legate de EJB, pe care le-am adus într-o altă recenzie trebuie să fie completate de citirea integrală a materialului acestei cărţi.
Versiunea de JPA avută în vedere este 2.0, care a apărut în anul 2009.
Ideea principală este că orice tabelă este mapată unei clase. Mai precis, o linie a unei tabele reprezintă o instanţiere a unei clase, numită şi entitate. O tabelă este, aşadar, o colecţie de clase. Acest proces de mapare este descris în capitolul "Object Relational Management". Adnotaţiile implicate sunt descrise tot aici. Pentru cei care cred că ştiu multe, o întrebare: ştiţi care este utilitatea anotaţiei @SecondaryTable? Desigur, detalii în carte la pagina 56, Capitolul 2.2.11.
Tabelele trebuie apoi relaţionate, iar relaţiile sunt mapate prin strategii speciale: atribute instanţă sau colecţii de instanţe ale claselor ce mapează tabele. Pentru creşterea performanţei trebuie avut în vedere tipul de colecţie folosită pentru maparea relaţiilor sau modul în care este reactualizată colecţia, relativ la modificările din baza de date. Toate acestea sunt descrise în capitolul "Relation Mapping".
Operaţiile pe baza de date se execută într-un context persistent, gestionat de un container, dacă discutăm de folosirea lui într-un server de aplicaţie. Contextul persistent gestionează stările entităţii. Unele dintre operaţii nu se pot executa în oricare dintre stări. Descrierea stărilor şi modalitatea de trecere între ele este facuta în "Entity Lifecycle".
Putem gestiona accesul concurenţial prin diverse modalităţi de blocare a entităţilor. Blocarea optimistă şi cea pesimistă sunt doar două astfel de modalităţi, descrise amanunţit în "Locking and Concurrency".
Revin la ideea de performanţă în dezvoltarea unei aplicaţii. Cum am putea să alegem între cele două moduri de gestionare a persitenţei: de către container sau de către aplicaţie (user)?
Un alt aspect favorabil al folosirii entităţilor este acela că datele pe care le stocăm în entităţi sunt practic gestionate intern, in memory. Trebuie să profităm de acest avantaj făcând cât mai multe dintre validări la nivelul middleware-ului. De asemenea, folosirea raţională a cache-ului duce la creşterea performanţei.
O altă importantă facilitate oferită de JPA este posibilitatea moştenirii entităţilor. Entităţile fiind clase sunt supuse regulilor POO, deci şi moştenirii. Partea cea mai interesantă este că clasele entitate derivate pot fi, la rândul lor mapate. Strategiile de mapare duc la alegerea între păstrarea regulilor Formei Normale 3 sau Boyce Codd, plata fiind un timp de acces mai mare, sau o tabelă cu câmpuri null, dar timp de acces mai redus.
Ultima şi cea mai importantă parte a materialului prezentat este legat de limbajul de interogare folosit pentru accesul la datele din baza de date. Java Persistence Query Language (JPQL) sau Criteria API sunt cele două variante dezbătute. Ele sunt şi cel mai frecvent utilizate.
Prima foloseşte un limbaj asemănător SQL-ului, cu modificări legate de utilizarea claselor în locul tabelelor. Prin introducerea alias-urilor entităţilor posibilitatea de folosire a SQL injection este aproape complet inhibată.
Cea de-a doua variantă foloseşte un limbaj obiect orientat pur. Dacă mai avem în vedere dependenţa de tip introdusă în Java, acest limbaj elimina practic multe dintre erorile ce pot apărea în faza de rulare.
Pe de-a întregul, expunerea conceptelor teoretice de către autor este însoţită de numeroase exemple şi fragmente de cod, care sunt foarte folositoare în înţelegerea deplină a functionalităţilor JPA.
Pâna în acest moment cartea poate fi privită ca un ghid complet de utilizare a framework-ului. Dacă avem în vedere însa şi completările aduse de ultimele capitole, cartea devine o referinţă pentru folosirea best practices sau design patterns.
O întrebare, pentru cei ce posedă cunoştinte mai avansate în acest domeniu: aţi folosit vreodată un "thread local pattern"? Un exemplu edificator este dat în această carte începând cu pagina 319, Capitolul 8.1.5.
Cartea se încheie cu un capitol numit "Resources" care furnizează detalii despre tehnologiile folosite.
Poate singura problemă pe care aţi putea să o aveţi în parcurgerea exemplelor acestei cărţi este dată de folosirea serverului de aplicaţie JBoss, în locul frecvent utilizatului Glassfish. Aceasta poate fi interpretată ca pe o provocare, iar diferenţele de configurare sunt minime, cu siguranţă putând fi realizate de oricare dintre cei cărora li se adresează această recenzie.
Aştept, ca întotdeauna, cu mare plăcere comentarii, păreri şi discuţii pe subiectele abordate în această lucrare.
Lectură placută!