Sunteți pe pagina 1din 21

Tutorial php si msql

Unul dintre cele mai importante avantaje pe care le ofera PHP este suportul sau pentru o varietate de sisteme de gestiune pentru baze de date, inclusiv MySQL, PostgreSQL, Oracle si Microsoft Access. In virtutea acestui suport, developerii PHP pot crea aplicatii Web orientate pe date la un timp si la un cost redus fata de cele pe care le presupun alternativele lor competitive. Folosind PHP si MySQL, developerii pot beneficia de economii substantiale asupra costurilor de licenta ale alternativelor comerciale, si de asemenea vor fi scutiti de uriasa cantitate de munca pe care au depus-o developerii PHP si MySQL pentru a se asigura ca cele doua pachete functioneaza impreuna fara nici o dificultate sau piedica. Si, din moment ce atat PHP si MySQL sunt proiecte open source, atunci cand le folosesti simultan, vei sti ca ti se ofera cea mai actualizata tehnologie disponibila. In aceasta editie a PHP 101, iti voi arata cum sa folosesti PHP pentru a extrage date dintr-o baza de date, iar apoi sa le folosesti pentru a construi dinamic o pagina Web. Pentru a incerca exemplele din acest tutorial, vei avea nevoie de o instalare functionala MySQL, pe care o poti obtine de pe site-ul MySQL http://www.mysql.com/. Daca esti familiarizat cu SQL (Limbaj de Interogare Structurat? limbajul folosit in interactiunea cu serverul unei baze de date) ti se va parea util, insa acest lucru nu este esential. Construirea blocurilor Pentru a utiliza MySQL si PHP, configuratia PHP trebuie sa includa suport pentru MySQL. In UNIX, acest lucru se realizeaza adaugand optiunea with-mysql la script-ul configure in momentul instalarii PHP in UNIX, si marcand PHP in bibliotecile-client MySQL. In Windows, bibliotecile client MySQL sunt construite in PHP 4 si au activare prestabilita. In PHP 5, fisierele pre-construite .dll sunt incluse in cazul distributiei Windows. Poti citi mai mult pe aceasta tema la http://www.php.net/manual/en/ref.mysql.php. Utilizatorii Unix ar trebui sa ia aminte la faptul ca PHP 4 ofera un set de biblioteci client MySQL care sunt activate automat; totusi, PHP 5 nu mai pune la dispozitie aceste biblioteci datorita unor probleme de licenta, asa ca va trebui sa le obtii, instalezi si activezi tu insuti. Ele sunt incluse in distributia MySQL si se instaleaza automat in momentul in care instalezi MySQL. Pentru a activa extensia MySQL, ext/mysql, adauga optiunea with-mysql la scriptul de configurare PHP. Pentru mai multe informatii pe tema acestei modificari, citeste http://www.php.net/manual/en/faq.databases.php#faq.databases.mysql.php5. Si, in sfarsit, (ca si cum toate aceste aspecte nu sunt destul de debusolante) PHP 5 mai aduce si o noua extensie MySQL, numita ext/mysqli (MySQL Improved). Poti folosi aceasta noua extensie pentru a accesa noile optiuni ale MySQL 4.1.2 sau ale unei versiuni si mai avansate, si pentru a te bucura de beneficiile unei viteze si securitati sporite. Pentru a activa aceasta extensie in UNIX, adauga optiunea with-mysqli scriptului configure al PHP, si marcheaza PHP in programul mysql_config care vine odata cu MySQL 4.1 sau cu o varianta superioara. Pentru utilizatorii Windows, exista o versiune pre-construita a ext/mysqli, inclusa in distributia win32 PHP. Pentru a-ti da seama de ce extensie ai nevoie, foloseste urmatorul procedeu empiric: Daca ai nevoie de noile optiuni din MySQL 4.1.2 sau dintr-o versiune mai avansata, sau daca folosesti o versiune mai veche a MySQL dar totusi doresti sa beneficiezi de perfectionarile la nivelul vitezei/securitatii din noua extensie, foloseste ext/mysqli. Daca nu apartii nici uneia dintre categoriile de mai sus, sau daca nici nu intelegi despre ce vorbesc, foloseste obisnuitul ext/mysql. Magnetism animal Orice baza de date MySQL se compune din unul sau mai multe tabele. Aceste tabele, care structureaza datele in randuri si coloane, sunt cele care organizeaza datele. Iata un exemplu care iti prezinta aspectul unui tabel tipic: +----+-----------+----------+ | id | country | animal | +----+-----------+----------+ | 1 | America | eagle | | 2 | China | dragon | | 3 | England | lion | | 4 | India | tiger | | 5 | Australia | kangaroo | | 6 | Norway | elk | Dupa cum poti remarca, un tabel imparte datele in randuri, cu o noua intrare (sau inregistrare) pe fiecare rand. Datele din fiecare rand sunt mai departe impartite in celule (sau campuri), fiecare dintre acestea

continand o valoare pentru un anumit atribut al datelor. De exemplu, daca te uiti la inregistrarea corespunzatoare tarii India, vei vedea ca inregistrarea este in mod evident impartita in campuri separate pentru numarul inregistrarii, numele tarii si animalul national. Randurile din cadrul unui tabel nu sunt aranjate intr-o ordine anume ? ele pot fi sortate alfabetic, dupa numar, sau dupa orice alt criteriu pe care alegi sa il specifici. Este deci necesar sa ai o metoda de identificare a anumitor inregistrari din tabel. In exemplul de mai sus, fiecare inregistrare este inidividualizata printr-un numar unic; acest camp unic se numeste cheia primara a tabelului. Vei folosi Limbajul de Interogare Structurat, SQL, pentru a interactiona cu serverul MySQL si pentru a-i spune sa creeze un tabel, sa marcheze un camp ca fiind primar, sa insereze inregistrari, sa editeze inregistrari, sa extraga inregistrari in principiu, orice implica manipularea datelor sau a bazei de date. Pentru a vedea cum functioneaza acest proces, studiaza urmatorul SQL, care creeaza tabelul de mai sus: CREATE DATABASE testdb; CREATE TABLE `symbols` ( `id` int(11) NOT NULL auto_increment, `country` varchar(255) NOT NULL default '', `animal` varchar(255) NOT NULL default '', PRIMARY KEY (`id`) ) TYPE=MyISAM; INSERT INTO `symbols` VALUES (1, 'America', 'eagle'); INSERT INTO `symbols` VALUES (2, 'China', 'dragon'); INSERT INTO `symbols` VALUES (3, 'England', 'lion'); INSERT INTO `symbols` VALUES (4, 'India', 'tiger'); INSERT INTO `symbols` VALUES (5, 'Australia', 'kangaroo'); INSERT INTO `symbols` VALUES (6, 'Norway', 'elk'); Poti introduce aceste comenzi fie interactiv, fie ne-interactiv prin programul client din linii de comenzi MySQL, pe care il poti rula navigand in directorul mysql/bin de pe shell-ul tau sau din DOS si tastand ? fara ?;? intrucat aceasta este o comanda shell fie mysql, fie mysql db_name daca vrei sa alegi o baza de date existenta cu care sa lucrezi. Citeste http://dev.mysql.com/doc/mysql/en/mysql.html pentru mai multe informatii despre cum sa folosesti clientul MySQL in linie de comanda, precum si tutorialul de pe http://www.melonfire.com/community/columns/trog/article.php?id=39 pentru a intelege ce indeplineste fiecare dintre comenzile SQL de mai sus. SQL seamana mult cu engleza vorbita, asa ca nu iti va lua foarte mult timp sa ti-l insusesti. Numai sa nu incerci sa transformi backtick-urile in ghilimele simple. Odata ce datele au fost importate, ruleaza o interogare rapida SELECT pentru a verifica daca totul merge asa cum ar trebui: mysql> SELECT * FROM `symbols`; +----+-----------+----------+ | id | country | animal | +----+-----------+----------+ | 1 | America | eagle | | 2 | China | dragon | | 3 | England | lion | | 4 | India | tiger | | 5 | Australia | kangaroo | | 6 | Norway | elk | +----+-----------+----------+ 6 rows in set (0.06 sec) In engleza, interogarea de mai sus inseamna arata-mi toate inregistrarile din tabelul intitulat symbols. Daca ai vizualizat aceleasi iesiri ca si mai sus, esti gata sa pornesti! Salut, baza de date! Acum, sa folosim PHP pentru a realiza exact acelasi lucru. Ai putea folosi PHP de la bun inceput pentru a construi baza de date, dar avand in vedere ca in cazul nostru, ea deja exista, vom lansa pur si simplu o interogare SELECT asupra bazei de date testdb, si vom afisa rezultatele intr-o pagina HTML: <html> <head> <basefont face="Arial"> </head>

<body> <?php // seteaza variabilele de acces la serverul MySQL $host = "localhost"; $user = "test"; $pass = "test"; $db = "testdb"; // deschide conexiunea $connection = mysql_connect($host, $user, $pass) or die ("Nu ma pot conecta la serverul MySQL!"); // selecteaza baza de date mysql_select_db($db) or die ("Nu pot selecta baza de date!"); // creeaza interogarea $query = "SELECT * FROM symbols"; // executa interogarea $result = mysql_query($query) or die ("Interogarea SQL contine o eroare: $query. ".mysql_error()); // verifica daca interogarea a returnat vreun rand if (mysql_num_rows($result) > 0) { // daca da // afiseaza randurile returnate unul dupa altul echo "<table cellpadding=10 border=1>"; while($row = mysql_fetch_row($result)) { echo "<tr>"; echo "<td>".$row[0]."</td>"; echo "<td>".$row[1]."</td>"; echo "<td>".$row[2]."</td>"; echo "</tr>"; } echo "</table>"; } else { // daca nu // afiseaza mesaj echo "Nu a fost gasit nici un rand!"; } // elibereaza memoria mysql_free_result($result); // inchide conexiunea cu baza de date mysql_close($connection); ?> </body> </html> Iata cum arata rezultatul: 1 America eagle 3 England lion 4 India tiger 5 Australia kangaroo 6 Norway elk Dupa cum poti vedea, utilizarea PHP-ului pentru a extrage date dintr-o baza de date presupune mai multi pasi, fiecare dintre acestia fiind de fapt o functie PHP pre-definita. Sa analizam fiecare pas: 1. Primul lucru care trebuie facut este sa specificam anumite informatii importante, care sunt necesare stabilirii unei conexiuni la serverul bazei de date. Aceste informatii cuprind numele serverului, numele de

utilizator si parola necesara pentru a o accesa, precum si numele bazei de date pe care se efectueaza interogarea. Toate aceste valori se construiesc in variabile PHP regulate. <?php $host = "localhost"; $user = "test"; $pass = "test"; $db = "testdb"; ?> 2. Pentru a incepe comunicarea cu un server de baze de date MySQL, trebuie sa deschizi o conexiune la acel server. Orice comunicare intre PHP si serverul bazei de date va avea loc prin intermediul acestei conexiuni. Pentru a initializa aceasta conexiune, PHP ofera functia mysql_connect():<?php $connection = mysql_connect($server, $user, $pass); ?> Toti parametrii din mysql_connect() sunt optionali, insa exista trei anume de care vei avea in general nevoie pentru a-i folosi, de pe orice computer de pe care lucrezi: numele serverului bazei de date, numele de utilizator si parola. In cazul in care serverul bazei de date si serverul Web sunt localizate fizic pe acelasi computer, poti folosi localhost ca nume pentru serverul bazei de date, aceasta fiind de fapt valoarea pre-stabilita pe care o furnizeaza PHP. mysql_connect() va returna un identificator de link, care este stocat in variabila $connection. Acest identificator este folosit in comunicarea cu baza de date. 3. Odata ce esti conectat la baza de date, trebuie sa selectezi o baza de date in vederea utilizarii, cu ajutorul functiei mysql_select_db(): <?php mysql_select_db($db) or die ("Nu pot selecta baza de date!"); ?> Acestei functii trebuie sa ii fie furnizat numele bazei de date ce va fi folosita pentru toate interogarile ulterioare. Un argument secundar optional ar fi identificatorul de link; daca nu se specifica nici un identificator, se considera ca atare ultimul link deschis. Daca ai doua sau mai multe conexiuni de baze de date deschise concomitent, este indicat sa specifici identificatorul de link ca argument secundar al mysql_select_db() ? si de asemenea al oricarei alte functii mysql_* din script, astfel incat PHP sa nu faca confuzii cu privire la unde trebuie sa foloseasca o conexiune, si unde pe cealalta. 4. Urmatorul pas este sa creezi o interogare si sa o executi. Acest lucru se realizeaza cu functia the mysql_query(). <?php $query = "SELECT * FROM symbols"; $result = mysql_query($query) or die ("Interogarea SQL contine o eroare: $query. ".mysql_error());?> Aceasta functie necesita de asemenea doi parametri: sirul de interogare si identificatorul de link pentru conexiune. Din nou, daca nu se specifica nici un identificator de link, se va folosi ultimul link deschis. In functie de reusita sau esecul interogarii, functia va returna true sau false; un eventual esec poate fi detectat prin intermediul clauzei or die() a functiei, iar functia mysql_error() poate fi folosita pentru a afisa mesajul corespunzator de eroare. 5. Daca mysql_query() se realizeaza cu succes, setul de rezultate returnat de catre interogare va fi stocat in variabila $result. Acest rezultat poate contine unul sau mai multe randuri sau coloane de date, in functie de interogarea ta. Poti extrage anumite subseturi ale setului de rezultate cu alte functii PHP, inclusiv cea folosita aici functia mysql_fetch_row() care extrage un singur rand de date ca o matrice numita $row. Campurile din acest rand pot fi apoi accesate folosind sistemul standard de notatii PHP. De fiecare data cand apelezi mysql_fetch_row(), se va returna urmatoarea inregistrare din setul de rezultate. Acest lucru face ca mysql_fetch_row() sa fie deosebit de potrivit pentru utilizarea intr-o bucla while() sau for(). <?php if (mysql_num_rows($result) > 0) { while($row = mysql_fetch_row($result)) { echo "<td>".$row[0]."</td>"; echo "<td>".$row[1]."</td>"; echo "<td>".$row[2]."</td>";

} } ?> Retine faptul ca apelarea mysql_fetch_row() este inclusa intr-un instructiune conditionala, care mai intai verifica daca s-a returnat vreun rand. Aceasta informatie este furnizata de catre functia mysql_num_rows(), care contine numarul de randuri returnat de catre interogare. Evident, poti folosi aceasta functie doar pentru interogarile care returneaza date, cum ar fi SELECT or SHOW. Ea nu este adecvata pentru a fi utilizata in cazul INSERT, UPDATE, DELETE sau alte interogari de acest tip. Mai exista si alte cateva alternative la mysql_fetch_row(), care vor fi explicate mai jos. In sfarsit, pentru ca fiecare set de rezultate returnat in urma unei interogari ocupa memorie, o practica buna este aceea de a utiliza functia mysql_free_result() pentru a elibera memoria folosita. Odata ce rezultatul este eliberat, si daca nu intentionezi sa mai rulezi alte interogari, poti inchide conexiunea la serverul MySQL cu mysql_close(). <?php mysql_free_result($result); mysql_close($connection); ?> Diverse abordari... De asemenea, poti folosi functiile PHP mysql_fetch_row() si list() pentru a obtine o simpla matrice de valori, iar apoi pentru a aloca aceste valori diverselor variabile o varianta a tehnicii prezentate in sectiunea precedenta. Arunca o privire (doar bucla while() se schimba): <html> <head> <basefont face="Arial"> </head> <body> <?php // seteaza variabilele de acces la serverul MySQL $host = "localhost"; $user = "test"; $pass = "test"; $db = "testdb"; // deschide conexiunea $connection = mysql_connect($host, $user, $pass) or die ("Nu ma pot conecta la serverul MySQL!"); // selecteaza baza de date mysql_select_db($db) or die ("Nu pot selecta baza de date!"); // creeaza interogarea $query = "SELECT * FROM symbols"; // executa interogarea $result = mysql_query($query) or die ("Interogarea SQL contine o eroare: $query. ".mysql_error()); // verifica daca interogarea a returnat vreun rand if (mysql_num_rows($result) > 0) { // daca da // afiseaza randurile returnate unul dupa altul echo "<table cellpadding=10 border=1>"; while(list($id, $country, $animal) = mysql_fetch_row($result)) { echo "<tr>"; echo "<td>$id</td>"; echo "<td>$country</td>"; echo "<td>$animal</td>"; echo "</tr>"; } echo "</table>"; }

else { // daca nu // afiseaza mesaj echo "Nu a fost gasit nici un rand!"; } // elibereaza memoria mysql_free_result($result); // inchide conexiunea cu baza de date mysql_close($connection); ?> </body> </html> In acest caz, functia list() este folosita pentru a aloca diverse elemente ale setului de rezultate unor variabile PHP, care sunt apoi folosite in redarea paginii. Poti folosi functia PHP mysql_fetch_assoc() pentru a reprezenta fiecare rand ca o matrice asociativa de perechi camp-valoare o variatie minora a tehnicii folosite mai sus: <html> <head> <basefont face="Arial"> </head> <body> <?php // seteaza variabilele de acces la serverul MySQL $host = "localhost"; $user = "test"; $pass = "test"; $db = "testdb"; // deschide conexiunea $connection = mysql_connect($host, $user, $pass) or die ("Nu ma pot conecta la serverul MySQL!"); // selecteaza baza de date mysql_select_db($db) or die ("Nu pot selecta baza de date!"); // creeaza interogarea $query = "SELECT * FROM symbols"; // executa interogarea $result = mysql_query($query) or die ("Interogarea SQLcontine o eroare: $query. ".mysql_error()); // verifica daca interogarea a returnat vreun rand if (mysql_num_rows($result) > 0) { // daca da // afiseaza randurile returnate unul dupa altul echo "<table cellpadding=10 border=1>"; while($row = mysql_fetch_assoc($result)) { echo "<tr>"; echo "<td>".$row['id']."</td>"; echo "<td>".$row['country']."</td>"; echo "<td>".$row['animal']."</td>"; echo "</tr>"; } echo "</table>"; } else { // daca nu // afiseaza mesaj echo "Nu a fost gasit nici un rand!"; } // elibereaza memoria

mysql_free_result($result); // inchide conexiunea cu baza de date mysql_close($connection); ?> </body> </html> Observa faptul ca in acest caz, valorile campurilor se acceseaza folosind numele campului in loc de index.Totusi, dintre toate alternativele, functia pe care o prefer cel mai mult este mysql_fetch_object(), care returneaza fiecare rand ca un obiect cu proprietati ce corespund numelor campurilor: <html> <head> <basefont face="Arial"> </head> <body> <?php // seteaza variabilele de acces la serverul MySQL $host = "localhost"; $user = "test"; $pass = "test"; $db = "testdb"; // deschide conexiunea $connection = mysql_connect($host, $user, $pass) or die ("Nu ma pot conecta la serverul MySQL!"); // selecteaza baza de date mysql_select_db($db) or die ("Nu pot selecta baza de date!"); // creeaza interogarea $query = "SELECT * FROM symbols"; // executa interogarea $result = mysql_query($query) or die ("Interogarea SQL contine o eroare: $query. ".mysql_error()); // verifica daca interogarea a returnat vreun rand if (mysql_num_rows($result) > 0) { // daca da // afiseaza randurile returnate unul dupa altul echo "<table cellpadding=10 border=1>"; while($row = mysql_fetch_object($result)) { echo "<tr>"; echo "<td>".$row->id."</td>"; echo "<td>".$row->country."</td>"; echo "<td>".$row->animal."</td>"; echo "</tr>"; } echo "</table>"; } else { // daca nu // afiseaza mesaj echo "Nu a fost gasit nici un rand!"; } // elibereaza memoria mysql_free_result($result); // inchide conexiunea cu baza de date mysql_close($connection); ?> </body> </html>

Aici, fiecare obiect $row este creat cu proprietati ce corespund numelor campurilor din acel rand. Valorile de pe rand pot fi accesate folosind notatia standard object->property. Daca esti genul caruia ii place sa profite de toate beneficiile, probabil te vei bucura de functia mysql_fetch_array(), care returneaza atat o matrice asociativa cat si o matrice cu indici numerici, o combinatie intre functia mysql_fetch_row() si mysql_fetch_assoc(). Citeste despre asta la http://www.php.net/manual/en/function.mysql-fetcharray.php. Pentru diferite gusturi... Daca folosesti PHP 5, poti face acelasi lucru folosind noua extensie ext/mysqli, care ofera o serie de noi optiuni. Aceasta extensie poate fi folosita in doua moduri: procedural (utilizand functii), si cu orientare pe obiecte (utilizand metode si proprietati de clasa). Studiaza scriptul urmator, care foloseste uses ext/mysqli intr-un mod procedural: <html> <head> <basefont face="Arial"> </head> <body> <?php // seteaza variabilele de acces la serverul MySQL $host = "localhost"; $user = "test"; $pass = "test"; $db = "testdb"; // deschide conexiunea $connection = mysqli_connect($host, $user, $pass, $db) or die ("Nu ma pot conecta la serverul MySQL!"); // creeaza interogarea $query = "SELECT * FROM symbols"; // executa interogarea $result = mysqli_query($connection, $query) or die ("Interogarea SQL contine o eroare: $query. ".mysqli_error()); // verifica daca interogarea a returnat vreun rand if (mysqli_num_rows($result) > 0) { // daca da // afiseaza randurile returnate unul dupa altul echo "<table cellpadding=10 border=1>"; while($row = mysqli_fetch_row($result)) { echo "<tr>"; echo "<td>".$row[0]."</td>"; echo "<td>".$row[1]."</td>"; echo "<td>".$row[2]."</td>"; echo "</tr>"; } echo "</table>"; } else { // daca nu // afiseaza mesaj echo "Nu a fost gasit nici un rand!"; } // elibereaza memoria mysqli_free_result($result); // inchide conexiunea mysqli_close($connection); ?> </body> </html>

Dupa cum poti vedea, seamana mult cu codul scris pentru ext/mysql. Singura diferenta reala cel putin la prima vedere este faptul ca numele functiilor incep in acest caz cu mysqli_* in loc de mysql_*. Desigur, mai exista destule diferente in profunzime: ext/mysqli este mai rapid, mai sigur si mai puternic decat obisnuita ext/mysql, incluzand de asemenea suport pentru instructiuni predefinite, seturi de rezultate, interogari multiple simultane, tranzactii si o groaza de alte lucruri marfa. Poti folosi de asemenea ext/mysqli intr-o modalitate de orientare pe obiecte, in care fiecare sarcina conectare, interogare, extragere reprezinta de fapt o metoda a obiectului mysqli(): <html> <head> <basefont face="Arial"> </head> <body> <?php // seteaza variabilele de acces la serverul MySQL $host = "localhost"; $user = "test"; $pass = "test"; $db = "testdb"; // creeaza obiectul mysqli // deschide conexiunea $mysqli = new mysqli($host, $user, $pass, $db); // verifica daca au aparut erori la conectare if (mysqli_connect_errno()) { die("Nu ma pot conecta la serverul MySQL!"); } // creeaza interogarea $query = "SELECT * FROM symbols"; // executa interogarea if ($result = $mysqli->query($query)) { // verifica daca interogarea a returnat vreun rand if ($result->num_rows > 0) { // daca da // afiseaza randurile returnate unul dupa altul echo "<table cellpadding=10 border=1>"; while($row = $result->fetch_array()) { echo "<tr>"; echo "<td>".$row[0]."</td>"; echo "<td>".$row[1]."</td>"; echo "<td>".$row[2]."</td>"; echo "</tr>"; } echo "</table>"; } else { // daca nu // afiseaza mesaj echo "Nu a fost gasit nici un rand!"; } // elibereaza memoria $result->close(); } else { // afiseaza mesaj de eroare echo "Interogarea SQL contine o eroare: $query. ".$mysqli->error; }

// inchide conexiunea $mysqli->close(); ?> </body> </html> Aici, noul cuvant cheie este folosit pentru a instantia un obiect din clasa mysqli, si pentru a da informatia de conexiune a constructorului de obiect (inclusiv numele bazei de date). Obiectul care rezulta, stocat in variabila $mysqli, va expune apoi metode si proprietati pentru a indeplini sarcini de interogare, extragere si procesare a randurilor, precum si de tratare a erorilor. Daca te uiti atent la cele doua scripturi de mai sus, vei remarca numeroasele similitudini dintre numele functiei si cel al metodei, precum si structura scriptului. Dintre cele doua, se recomanda totusi metoda de orientare pe obiecte, mai ales in daca avem in vedere noul model de obiect din PHP 5. Alte cateva diferente importante de retinut: Cu ext/mysqli, poti include numele bazei de date in argumentele asociate functiei mysqli_connect() sau constructorului mysqli(). Atunci cand apelezi mysqli_query() sau metoda de interogare query() a obiectului mysqli, identificatorul de link este obligatoriu, si nu optional.

l l l l l l l l

Baze de date relationale: generalitati Notiuni generale de SQL Conectarea la o baza de date folosind JDBC: Connection, Statement, ResultSet Maparea tipurilor de date SQL / Java Inserarea / stergerea / modificarea datelor Parametrizarea unui query Folosirea tranzactiilor Framework-uri de acces la baza de date (Hibernate)

Baze de date relationale: generalitati O baza de date relationala este o in esenta o colectie de tabele Fiecare tabel contine inregistrari (echivalente rindurilor unui tabel) Fiecare inregistrare este alcatuita din cimpuri (echivalentul coloanelor); toate inregistrarile unui tabel au aceleasi cimpuri, definite al constructia tabelului Fiecare cimp are un nume si un tip; tipurile de date sint de 4 categorii principale: numerice, de tip caracter, date si de tip binar Orice cimp al unei inregistrari poate avea o valoare speciala, NULL (cimpul nu contine nimic) Asupra cimpurilor unui tabel se pot face limitari in ceea ce priveste valorile continute (de exemplu: nu pot fi null) Identificarea unica a unei inregistrari intr-un tabel se face prin chei. O cheie e unul din cimpurile inregistrarii, marcat explicit ca fiind cheie (cheie primara). Un astfel de cimp nu poate fi null si are o valoare unica pentru fiecare inregistrare Legatura intre tabele se face prin chei straine; o cheie straina este un cimp normal (care nu e cheie primara) al unei inregistrari, care indica un cimp cheie primara din alt tabel. Legatura se face prin valoare (cheia straina din tabel are aceeasi valoare cu cheia primara a inregistrarii corespunzatoare lui din tabelul referit)

Notiuni generale de SQL


- tipuri de date Se vor prezenta in continuare tipurile standard de date. Fiecare server de baze de date are si alte tipuri de date proprii. Tipul de date Comentarii SMALLINT Intreg (de obicei pe 2 octeti) INT Intreg (de obicei pe 4 octeti) DOUBLE Numar in virgula mobila, dubla precizie NUMERIC (p,s) Numar in virgula mobila cu un numar total de p cifre din care s dupa virgula CHAR(x) Sir de caractere de lungime x VARCHAR(x) Sir de caractere de lungime intre 0 si x DATE Data calendaristica (an, luna,zi) TIMESTAMP Valoare intreaga reprezentind o data (an, luna, zi, ora, minut, secunda, uneori milisecunda) BINARY(x) Date binare cu dimensiunea de x octeti BLOB Date binare cu dimensiunea maxima de 2 megaocteti

- crearea unei baze de date; conectarea la o baza de date Pentru a putea crea tabele va trebui creata intii o baza de date. Sintaxa SQL este CREATE DATABASE nume [optiuni] Optiunile depind de serverul de baze de date folosit. O optiune generala este CHARACTER_SET (poate fi latin, unicode etc. in functie de serverul de base de date folosit) Exemplu: CREATE DATABASE promanagement; Urmeaza conectarea la baza de date. De asemenea sirul de conectare depinde de serverul de baze de date folosit. Pentru MySQL, comanda este USE database_name. Pentru IBM DB2, comanda este CONNECT TO database_name USER user_name USING password. - crearea unui tabel Sintaxa pentru crearea unui tabel este CREATE TABLE name ( columns ); Numele tabelului poate fi prefixat de catre numele bazei de date sau al schemei curente (in IBM DB2 numele schemei depinde de utilizatorul conectat; in MySQL versiunea 5 si mai noi schema este echivalenta cu baza de date).Exemplu: CREATE TABLE promanagement.task ( TaskID INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, Name VARCHAR(45), Description VARCHAR(255), PRIMARY KEY (`TaskID`) ); Daca inainte de a crea tabelul s-a efectuat conectarea la baza de date, numele schemei nu mai este obligatoriu. Exemplu: CREATE TABLE task ( TaskID INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, Name VARCHAR(45), Description VARCHAR(255), PRIMARY KEY (`TaskID`)); Nota: unele cimpuri pot avea valori implicite. In exemplul de mai sus se poate scrie Name VARCHAR(45) DEFAULT null - modificarea unui tabel Se foloseste comanda ALTER TABLE. Se pot adauga coloane, sterge coloane sau adauga constringeri de tipul cheie straina. Exemplu: ALTER TABLE task ADD COLUMN comments VARCHAR(100); Pentru a sterge o coloana:ALTER TABLE task DROP COLUMN comments; Exemplu de adaugare de constringere: ALTER TABLE task ADD CONSTRAINT FOREIGN KEY(newtaskid) REFERENCES newtask(newtaskid); Nota: O constringere se poate adauga si la crearea tabelului Nota: Mecanismul de verificare a cheilor straine din MySQL se poate dezactiva folosind comanda SET FOREIGN_KEY_CHECKS = 0 Pentru verificarea constringerilor se poate rula comanda din exemplul de mai jos: SHOW TABLE STATUS FROM promanagement LIKE 'task' - selectii simple Pentru selectie se foloseste comanda SELECT [lista_cimpuri] FROM tabel Exemplu: SELECT taskid, description FROM task; Pentru ca in tabelul rezultat sa se inlocuiasca numele coloanelor cu alt nume mai sugestiv se poate folosi un alias. Exemplu: SELECT taskid as ID, description as 'Descrierea taskului' FROM task - adaugarea de inregistrari Se foloseste comanda INSERT INTO table_name( lista_cimpuri) VALUES (lista_valori) Exemplu: INSERT INTO task(name,description) VALUES('task1','primul task inserat'); In cazul in care nu se specifica lista de cimpuri vor trebui date valori pentru toate cimpurile, in ordinea in care au fost definite. - stergerea de inregistrari Se foloseste comanda DELETE FROM tabel WHERE conditie. Exemplu: DELETE FROM task WHERE name = task1; Nota: daca se doreste stergerea unui singur rind, se recomanda stergerea dupa cheia primara - selectii complexe, din mai multe tabele; tipuri de join-uri, selectii imbricate Pentru exemplificare se vor considera urmatoarele tabele:

Employees: Employee_ID Name 01 Ion Popescu 02 Ileana Ionescu 03 Andrei Vlad Orders: Prod_ID Product Employee_ID 234 Imprimanta 01 657 Birou 03 865 Scaun 03 Cel mai simplu tip de selectie din mai multe tabele (din punct de vedere al sintaxei) este utilizarea instructiunii: SELECT lista_cimpuri FROM tabel1, tabel2, . tabeln WHERE conditii Exemplu: SELECT Employees.Name, Orders.Product FROM Employees, Orders WHERE Employees.Employee_ID=Orders.Employee_ID Acesta va afisa cimpul Name din Employees si cimpul Product din Orders pentru toate rindurile unde este satisfacuta relatia Employees.Employee_ID=Orders.Employee_ID. Nota: daca un cimp din cadrul relatiei contine valori NULL, rindul corespunzator nu va aparea in rezultate. La acest select, pe linga conditia de egalitate, se pot adauga si conditii de filtrare. Exemplu: SELECT Employees.Name FROM Employees, Orders WHERE Employees.Employee_ID=Orders.Employee_ID AND Orders.Product=Birou INNER JOIN: Este o alta sintaxa pentru instructiunea SELECT de mai sus, pentru doua tabele. Se face produsul cartezian al celor doua tabele (toate combinatiile de linii) si se aleg ca rezultat cele care indeplinesc conditia. Rindurile care au valoarea NULL in cimpurile din conditie sint eliminate din rezultat. Sintaxa este: SELECT lista_cimpuri FROM tabel1 INNER JOIN tabel 2 ON conditie Exemplu: SELECT Employees.Name, Orders.Product FROM Employees INNER JOIN Orders ON Employees.Employee_ID=Orders.Employee_ID Se poate adauga si o clauza WHERE: SELECT Employees.Name, Orders.Product FROM Employees INNER JOIN Orders ON Employees.Employee_ID=Orders.Employee_ID WHERE Orders.Product=Birou LEFT JOIN Se face produsul cartezian al celor doua tabele si se aleg ca rezultat rindurile care indeplinesc conditia din clauza ON a instructiunii. Aditional, se aleg si rindurile din primul tabel care nu indeplinesc conditia. Sintaxa este SELECT lista_cimpuri FROM tabel1 LEFT JOIN tabel 2 ON conditie Exemplu: SELECT Employees.Name, Orders.Product FROM Employees LEFT JOIN Orders ON Employees.Employee_ID=Orders.Employee_ID Se vor selecta toti angajatii din tabelul Employee, chiar daca unii (in acest caz angajatul cu ID = 2) nu au inregistrari corespondente in tabelul Orders. Cimpurile din al doilea tabel pentru care nu exista valori vor fi umplute cu NULL. RIGHT JOIN Se face produsul cartezian al celor doua tabele si se aleg ca rezultat rindurile care indeplinesc conditia din clauza ON a instructiunii. Aditional, se aleg si rindurile din al doilea tabel care nu indeplinesc conditia. Sintaxa esteSELECT lista_cimpuri FROM tabel1 RIGHT JOIN tabel 2 ON conditie Exemplu: SELECT Employees.Name, Orders.Product FROM Employees RIGHT JOIN Orders

ON Employees.Employee_ID=Orders.Employee_ID Ordonarea inregistrarilor Se foloseste clauza ORDER BY cimp plasata la sfirstitul instructiunilor de selectie. Exemplu: SELECT * FROM task ORDER BY Name Daca se doreste ordonarea descrescatoare, se poate scrie: SELECT * FROM task ORDER BY Name DESC Pentru a se ordona dupa mai multe cimpuri, dupa clauza ORDER BY se scrie o lista de cimpuri. Dupa fiecare cimp din lista se poate adauga sau nu DESC. Exemplu: SELECT * FROM task ORDER BY Name DESC, Description

Conectarea la o baza de date folosind JDBC: Connection, Statement, ResultSet


Primul pas pentru conectarea la o baza de date dintr-un program Java este obtinerea driver-ului JDBC pentru serverul de baze de date respectiv. Acesta este de obicei o arhiva JAR sau o colectie de clase/arhive si, dupa copierea pe masina unde ruleaza aplicatia Java, va trebui adaugat in CLASSPATH. Fiecare driver are propria sa metoda de a defini conexiunea la baza de date. Sirul de conexiune este asemanator unui URL de pagina Web, dar protocolul nu este HTTP ci JDBC.In continuare se va exemplifica conectarea la un server MySQL:Driver-ul este com.mysql.jdbc.Driver, iar sirul de conexiune este de forma jdbc:mysql://HOST/DATABASE, unde HOST este numele masinii pe care ruleaza serverul, iar DATABASE este numele bazei de date la care se face conexiunea. In cazul in care serverul ruleaza pe aceeasi masina ca si programul Java, HOST va lua valoarea localhost. Conexiunea la o baza de date este un obiect de tipul java.sql.Connection. Exemplul de mai jos ilustreaza conectarea la o baza de date si obtinerea obiectului Connection: Connection conn = null; String userName = "root"; String password = "promanagement"; String url = "jdbc:mysql://localhost/promanagement"; Class.forName ("com.mysql.jdbc.Driver").newInstance (); conn = DriverManager.getConnection (url, userName, password); Dupa folosire, obiectul Connection trebuie inchis prin apelarea metodei close(). De obicei acest apel se face intr-un bloc finalize.Pentru a putea executa comenzi SQL, pe baza obiectului Connection se creeaza un obiect java.sql.Statement. Exemplu: Statement st = conn.createStatement(); Dupa folosire, obiectul Statement trebuie inchis prin apelarea metodei close(). De obicei acest apel se face intr-un bloc finalize. Un obiect de tipul Statement poate executa comenzi SQL de extragere de date (prin intermediul metodei executeQuery(..)) si de actualizare a continutului bazei de date (prin intermediul metodei executeUpdate()). Metoda executeQuery returneaza un obiect de tipul java.sql.ResultSet, care contine datele citite din baza de date. Exemplu:String sql = "SELECT * FROM task"; ResultSet rs = st.executeQuery(sql); Navigarea printr-un ResultSet Un obiect de tipul ResultSet poate fi modificat in doua privinte: Felul de parcurgere a datelor obtinute Felul in care schimbarile datelor se reflecta in obiectul ResultSet Tipurile de ResultSet sint urmatoarele (sint constante definite in interfata ResultSet): TYPE_FORWARD_ONLY: cursorul se poate deplasa doar inainte TYPE_SCROLL_INSENSITIVE: cursorul se poate deplasa in ambele directii si poate fi pozitionat absolut; schimbarile facute in baza de date nu se reflecta in obiect dupa ce acesta a fost creat TYPE_SCROLL_SENSITIVE: cursorul se poate deplasa in ambele directii si poate fi pozitionat absolut; schimbarile facute in baza de date se reflecta in obiect dupa ce acesta a fost creat O utilizare tipica a lui ResultSet include preluarea de date si parcurgerea liniara a rezultatelor ca in exemplul de mai jos:ResultSet rs = st.executeQuery(sqlString); while(rs.next()) Obtinerea de date din rindul curent se face cu ajutorul metodelor get...() ale obiectului ResultSet. De exemplu, metoda getString(..) intoarce o valoare de tipul String, metoda getInt(..) intoarce o valoare de tipul int etc. Metodele get...() au doua versiuni: una care ia ca parametru un String reprezentind numele coloanei si alta care ia ca parametru un int, reprezentind numarul coloanei in tabela de rezultate (1 este prima coloana din stinga, 2 a doua

Maparea tipurilor de date SQL / Java Metodele get() ale obiectului ResultSet incearca sa interpreteze datele din coloana data ca parametru si intorc un tip de date Java. Interpretarea nu depinde de tipul SQL al datelor din coloana respectiva, ci de metoda apelata. De exemplu: Coloana TaskID contine un INTEGER cu valoarea 1. rs.getInt(TaskID) va intoarce intregul 1 rs.getString(TaskID) va intoarce un String cu valoarea 1. Daca tipul de date nu poate fi convertit se arunca o exceptie. Tabelul urmator indica tipurile de date SQL si maparea lor corespunzatoare in tipuri de date Java: Tipul de date SQL Tipul de date Java Functia apelata din ResultSet SMALLINT short getShort() INT int getInt() DOUBLE double getDouble() NUMERIC (p,s) java.math.BigDecimal getBigDecimal() CHAR(x) java.lang.String getString() VARCHAR(x) java.lang.String getString() DATE java.sql.Date getDate() TIMESTAMP java.sql.Timestamp getTimestamp() BINARY(x) byte[] getBytes() BLOB java.io.InputStream getBinaryStream()

Inserarea / stergerea / modificarea datelor Operatiunile de modificare a datelor se efectueaza cu ajutorul metodei executeUpdate() a lui ResultSet. Aceasta intoarce un intreg care reprezinta numarul de rinduri afectate in cazul operatiunilor INSERT, DELETE, UPDATE sau 0 pentru celelalte operatiuni (cum ar fi DROP TABLE). In caz de eroare se arunca exceptii. Exemplu: String sql = "INSERT INTO task(Name,Description) VALUES(\"automatic\",\"this task was inserted automatically\")"; int i = st.executeUpdate(sql);

Parametrizarea unui query In loc de a construi sirul SQL ca String si a-l pasa unui Statement, exista posibilitatea de a crea un sir SQL generic, in care valorile cimpurilor sa fie inserate ulterior. Aceasta abordare permite o mai mare claritate a codului (evitarea folosirii repetate a caracterelor de escape pentru string-uri, scurtarea comenzii SQL etc.) si de asemenea imbunatateste viteza de executie cind se aplica aceeasi comanda SQL pentru mai multe seturi de parametri. In acest scop se foloseste, in locul lui java.sql.Statement, un obiect de tipul java.sql.PreparedStatement. Cu un astfel de obiect se lucreaza in doi pasi: Se creeaza prin apelarea metodei prepareStatement a obiectului Connection Se inlocuiesc parametrii cu valorile efective, apelind metodele set () ale obiectului PreparedStatement creat Exemplu: String sql = "SELECT * FROM task WHERE Name = ?"; st = conn.prepareStatement(sql); st.setString(1, "automatic"); Valorile efective sint inlocuite in comanda SQL prin caracterul ? (semnul intrebarii). Pentru fiecare tip de date Java exista o metoda specifica a lui PreparedStatement pentru a seta un atribut. Ele au nume corespunzatoare metodelor get din ResultSet. Exista deci un setString, setInt etc. Primul parametru al unei metode set() este numarul parametrului. Se paseaza argumentul 1 pentru a seta primul atribut, 2 pentru a seta al doilea atribut etc.

Folosirea tranzactiilor In mod normal, fiecare executie a metodei executeUpdate(..) a lui ResultSet se reflecta imediat in baza de date. Exista cazuri in care acest lucru nu este dorit. De exemplu cind trebuie actualizate mai multe tabele care depind unele de altele. Daca s-a actualizat un numar de tabele si apoi se produce o exceptie, datele vor ramine inconsistente. Clasele JDBC din limbajul Java pun la indemina un mecanism pentru a executa atomic un numar de comenzi SQL. Atomic inseamna ca se vor executa fie toate instructiunile, fie nici una. Acest mecanism poarta numele de tranzactii. Obiectul Connection creat pentru conexiune dispune de metoda setAutoCommit(..) care poate modifica comportamentul la apelarea metodelor executeUpdate(). Prin apelarea lui setAutoCommit(false) executia metodelor executeUpdate() nu se va reflecta imediat in baza de date, ci se va astepta apelarea metodei update() a obiectului Connection. In caz de eroare (de exemplu in blocul catch) se poate apela metoda rollback() a obiectului Connection pentru a anula toate modificarile facute. Pseudocodul unei metode care foloseste tranzactii este: Connection.setAutoCommit(false); try catch(SQLException e) Nota: exista optiunea de a efectua rollback doar la portiuni dintr-o tranzactie. In interiorul tranzactiei se poate apela metoda setSavepoint a obiectului Connection pentru a seta un punct de intoarcere, iar in cazul unei exceptii, in functie de anumite conditii, se poate apela rollback(Savepoint sp) a obiectului Connection pentru a reveni la un savepoint creat anterior.

Framework-uri de acces la baza de date (Hibernate) Hibernate este un framework care permite stocarea obiectelor Java intr-o baza de date relationala. Stocarea se face obiectual, apelind metode de genul object.save() sau object.load(), fara a fi nevoie sa se scrie comenzi SQL. Hibernate este disponibila spre descarcare la http://www.hibernate.org/ Pentru a lucra cu Hibernate se vor introduce in proiect bibliotecile hibernate3.jar, jta.jar, dom4j.jar, log4j*.jar, commons-logging.jar, cglib*.jar, asm.jar, asm-attrs.jar, antlr.jar Primul pas in lucrul cu Hibernate este crearea claselor care vor fi stocate in baza de date. Acestea sint simple clase Java care respecta conventia JavaBeans. Exemplu: package promanagement.hibernate; import java.sql.Date; public class Task public Long getId() private void setId(Long id)

public String getDescription() public void setDescription(String description) public String getName() public void setName(String name) public Date getTaskDate() public void setTaskDate(Date taskDate) } Se pot face urmatoarele observatii: S-a definit un cimp id care va folosi drept cheie primara pentru clasa persistata. Setter-ul pentru acest cimp este declarat private pentru a nu putea fi folosit de catre clasele proiectului. Hibernate il va putea insa accesa. S-a definit un constructor fara parametri si fara corp. Acesta este necesar pentru ca Hibernate sa poata instantia obiectul Odata clasa creata, ea va trebui inregistrata cu Hibernate, pentru a putea fi salvata in baza de date. In acest scop se va crea fisierul Task.hbm.xml in acelasi director ca si fisierul Task.java. Acesta va contine maparea cimpurilor in baza de date. Exemplu: <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernatemapping-3.0.dtd"> <hibernate-mapping> <class name="promanagement.hibernate.Task" table="TASK"> <id name="id" column="ID"> <generator class="native"/> </id> <property name="name"/> <property name="description" column="DESCRIPTION"/> <property name="taskDate" type="timestamp" column="TASKDATE"/> </class> </hibernate-mapping> Se remarca in primul rind maparea clasei Task in tabela TASK. Urmeaza specificarea cheii primare, prin tag-ul id. Tag-ul id, ca si tag-ul property care indica un cimp din clasa, pot avea atributul optional column care indica numele coloanei in baza de date. Daca acest atribut lipseste, numele coloanei este egal cu numele cimpului. In ceea ce priveste atributul type: Hibernate incearca sa mapeze tipul de date Java al cimpului respectiv la tipul de date SQL corespunzator. Atributul type este folosit doar cind maparea este ambigua. De exemplu, tipul java.sql.Date poate fi translatat in tipul SQL DATE, sau in tipul SQL TIMESTAMP. Dupa configurarea individuala a fiecarei clase mapate urmeaza configurarea globala a Hibernate. Aceasta consta in configurarea serverului de baze de date, a URL-ului bazei de date, a tipului de cache folosit etc. Fisierul de configurare se numeste hibernate.cfg.xml si se plaseaza in radacina directorului cu surse Java al proiectului. Exemplu de fisier de configurare:

<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernateconfiguration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- Database connection settings --> <property name="connection.driver_class"> com.mysql.jdbc.Driver </property> <property name="connection.url"> jdbc:mysql://localhost/promanagement </property> <property name="connection.username"> root </property> <property name="connection.password"> promanagement </property> <!-- JDBC connection pool (use the built-in) --> <property name="connection.pool_size"> 1 </property> <!-- SQL dialect --> <property name="dialect"> org.hibernate.dialect.MySQLDialect </property> <!-- Enable Hibernate's automatic session context management --> <property name="current_session_context_class"> thread </property> <!-- Disable the second-level cache --> <property name="cache.provider_class"> org.hibernate.cache.NoCacheProvider </property> <!-- Echo all executed SQL to stdout --> <property name="show_sql">true</property> <!-- Drop and re-create the database schema on startup --> <property name="hbm2ddl.auto"> create </property> <mapping resource="promanagement/hibernate/Task.hbm.xml"/> </session-factory> </hibernate-configuration> Pentru a putea lucra cu Hibernate este necesara initierea unei sesiuni (asemanatoare unei tranzactii JDBC). Ea este reprezentata de un obiect de tip org.hibernate.Sesssion. Acesta nu se instantiaza direct, ci prin intermediul unui obiect org.hibernate.SessionFactory. O sesiune incepe in momentul in care se apeleaza prima data metoda getCurrentSession() a lui SessionFactory si se termina in momentul in care se executa un session.getTransaction().commit() sau un session.getTransaction().rollback(). De asemenea, pentru initierea explicita a unei tranzactii se poate apela session.beginTransaction() Pentru crearea unui SessionFactory dintr-un fisier hibernate.cfg.xml se apeleaza sessionFactory = new Configuration().configure().buildSessionFactory(); Exemplu de salvare a unui obiect in baza de date: Session session = HibernateUtil.getSessionFactory().getCurrentSession();

session.beginTransaction(); Task task = new Task(); task.setName("title"); task.setDescription("this is a task generated by Hibernate"); session.save(task); session.getTransaction().commit();

Incarcarea din baza de date: Hibernate foloseste un limbaj intermediar de query, numit HQL. El este asemanator cu SQL-ul, dar utilizeaza entitatile definite in Hibernate (obiectele mapate) in locul tabelelor din baza de date. Exemplul urmator prezinta modalitatea de a incarca toate inregistrarile dintr-un tabel (echivalent: toate obiectele de un anume tip serializate) Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); List result = session.createQuery("from Task").list(); session.getTransaction().commit();