Java DataBase Connectivity



Java DataBase Connectivity

 

4.1. Introducere0

Odata cu evolutia tehnologiilor imformatice s-a ajuns la standardizarea unui limbaj SQL (Structured Query Language) cu ajutorul caruia se pot prelucra datele stocate in bazele de date. Ca urmare a standardizarii in 1992 a limbajului SQL, un program poate comunica cu o baza de date fara a avea nevoie de schimbarea comenzilor SQL. Cu toate acestea, din pacate fiecare producator de SGBD a dezvoltat propriile extensii ale SQL si ofera o interfata diferita pentru manipularea datelor.



 

Ce este ODBC? De ce ODBC?

ODBC(Open Data Base Connectivity) reprezinta o interfata consistenta pentru prelucrarea detelor, indiferent de formatul in care acestea sunt stocate. ODBC reprezinta o colectie de functii apelabile din limbajul C, fiecare functie avand un nume bine determinat si o colectie de parametrii clar stabiliti. Spre exemplu functia createTable() permite crearea unei tabele in orice format. La fel si functia getMetaData() stie sa citeasca metadatele (informatii despre structura unei baze de date) indiferent de faptul ca respectiva baza de date a fost creata cu Oracle, Access, Informix sau FoxPro. Ca urmare a acestor avantaje majore, desi ODBC a fost initial conceput ca un standard pentru PC, astazi el a devenit un standard adoptat de toate platformele.

Desi o buna parte din problemele initiale au fost rezolvate, a aparut o noua problema aceasta fiind portabilitatea. Limbajul C++ permite scrierea unei aplicatii performante pentru manipularea datelor, gratie si ODBC-ului, numai ca aplicatia respectiva trebuie rescrisa integral pentru a lucra pe o alta platforma. Acest impas apare datorita faptului ca limbajul C++ nu este unul complet (in cazul limbajului Java se stie foarte clar faptul ca o variabila de tip int va ocupa intodeauna 32 de biti, indiferent de platforma). Unul dintre avantajele pe care le ofera Java este portabilitatea. Aceasta inseamna ca putem rula un program scris in Java pe orice platforma fara sa fie nevoie ca programul sa fie recompilat. Printre bibliotecile implementate pe platformele care ruleaza Java se afla si cea care permite accesul la bazele de date din Java: JDBC (Java DataBase Connectivity). Aceasta biblioteca reprezinta echivalentul lui ODBC din C.

 

De ce JDBC?

Pana acum producatorii erau preocupati de dezvoltarea si livrarea driverelor ODBC pentru sistemele lor de gestiune. Astazi ei se orienteaza spre producerea de drivere JDBC. Pentru a permite utilizarea vechilor baze de date firma Sun pune la dispozitia utilizatorilor pachetul java.sql care reprezinta un translator intre apelurile JDBC si apelurile ODBC. Folosirea limbajului Java in conjunctie cu JDBC ofera o solutie cu adevarat portabila pentru scrierea aplicatiilor care lucreaza cu baze de date.

In JDBC 1.0 API exista patru categorii de drivere:

  1. JDBC – ODBC bridge este o interfata intre JDBC driver manager si ODBC, care a devenit un standard. Acest tip de driver este deschis spre mai multe SGBD-uri. In acest caz, codul binar al ODBC trebuie incarcat pe fiecare calculator client.

  2. Native – API este o interfata intre JDBC driver manager si interfata client a SGBD-ului. Spre deosebire de primul tip care este deschis, acesta este dedicat unui SGBD. In schimb este mai performant, deoarece numarul de interfete pentru a accesa baza de date se reduce. Un program Java care realizeaza un driver de tip 2 trebuie sa incarce in memorie codul nativ al interfetei client a SGBD-ului in cauza.

  3. JDBC – Net este o interfata intre JDBC driver manager si un serviciu specializat de acces la date (middleware) care se executa pe un alt server. Protocolul de comunicare intre JDBC driver manager si middleware este transparent din punct de vedere al programatorului de aplicatii.

  4. Native – protocol este o interfata intre JDBC driver manager si interfata server SGBD, incorporand complet interfata SGBD . Astfel un program Java care este utilizat ca driver de tip 4 trebuie sa deschida o conexiune in retea cu un calculator pe care se executa o interfata server a SGBD-ului care ruleaza pe calculatorul client.

 

4.2. Utilizarea JDBC 1.0 API

 

Pentru orice aplicatie care lucreaza cu baze de date, exista cateva etape specifice care trebuie urmate. Dintre acestea enumeram:

· Crearea unei baze de date.

Inainte de prelucrarea datelor stocate trebuie sa avem la dispozitie “containerul” care va contine toate aceste date. Adica este nevoie ca baza de date sa fie creata. Crearea unei baze de date se poate realiza fie din afara unei aplicatii Java, fie in interiorul aplicatiei Java prin transmiterea comenzilor SQL. Avantajul Java consta in faptul ca nu este nevoie sa modificam codul in functie de formatul de stocare.

· Conectarea la o baza de date.

Pentru a putea accesa o baza de date este nevoie ca aplicatia Java sa se “conecteze” la sursa. In spatele acestei sintagme se ascunde urmatorul aspect: o baza de date este stocata intr-un anumit format. Datele stocate intr-un anumit format sunt accesate cu ajutorul unui anumit format, fie el ODBC sau JDBC. In momentul in care aplicatia noastra doreste sa se conecteze la baza de date este nevoie de alegerea driverului potrivit si incarcarea lui in memorie.

· Scrierea in baza de date.

Operatiile de introducere a datelor in baza de date pot avea loc atat din afara unei aplicatii Java cat si prin intermediul unor comenzi SQL specifice transmise din cadrul unei aplicatii Java. Oricare ar fi calea aleasa, una dintre comenzile utilizate este INSERT INTO NumeTabela DATA.

· Citirea selectiva a datelor.

De asemenea, operatiile de citire selectiva dintr-o baza de date, pot avea loc atat din afara unei aplicatii Java, cat si din interiorul acesteia, transmitand comanda SQL ca si parametru unei metode care caracterizeaza comportamentul unei clase din pachetul java.sql. Comanda SQL ar putea avea forma SELECT DATA FROM NumeTabela.

Putem spune ca unele dintre obiectivele stocarii datelor este ca prin prelucrarea lor sa obtinem informatii, sau altfel spus sa obtinem sistematizarea lor. Acesta este de fapt obiectul final.

JDBC permite dezvoltarea unor programe client Java (aplicatii stand-alone sau applet-uri) care acceseaza baze de date prin SGBD-ul acestora. In acest sens, un program Java, care utilizeaza JDBC este structurat pe doua straturi:

  • primul este orientat spre aplicatia Java, se numeste JDBC driver manager si este in ultima instanta un obiect Java la care se adreseaza mai multe obiecte ale aplicatiei.

  • al doilea este orientat spre SGBD si necesita drivere JDBC specifice bazelor de date la care aplicatia client trebuie sa aiba acces. JDBC permite accesul simultan al unei aplicatii Java la mai multe baze de date.

 

Figura 4.1. JDBC si aplicatiile Java

 

 

4.3. Structura JDBC

 

In scopul accesarii unei baze de date Oracle, Access, Informix, etc. producatorii driverelor trebuie sa implementeze o colectie de clase si metode definite in cadrul a opt interfete din biblioteca JDBC . Asa cum pot fi vazute in figura de mai jos (Figura 4.2) acestea sunt:

 

Figura 4.2. Interfete din biblioteca JDBC

 

· java.sql.CallableStatement – trebuie sa permita executarea procedurilor stocate in baza de date.

· java.sql.Connection – in contextul unei conexiuni cu baza de date se executa comenzile SQL si sunt returnate rezultatele.

· java.sql.DatabaseMetaData – permite returnarea informatiilor referitoare la baza de date, numarul de tabele ce fac parte din baza de date, structura tabelelor din baza de date, cate campuri cuprinde o anumita tabela, etc.

· java.sql.Driver – cadrul oferit de JDBC permite utilizarea a multiple drivere. Orice astfel de driver trebuie sa ofere o clasa care sa implementeze aceasta interfata. La cererea unei aplicatii de conectare la o baza de date, clasa DriverManager va interoga fiecare driver daca poate realiza conexiunea cu sursa de date. Aceasta interogare se poate realiza numai daca aceasta poate implementa metode din interfata Driver.

· java.sql.PreparedStatement – un enunt SQL este precompilat si stocat intr-un obiect de tip PreparedStatement. Acest obiect poate fi utilizat mai apoi pentru executarea de mai multe ori a respectivului enunt cu o mult mai mare eficienta.

· java.sql.ResultSet – metodele acestei interfete permit accesarea tabelei generate in urma executarii unei interogari SQL.

· java.sql.ResultSetMetaData – un astfel de obiect poate fi utilizat pentru a afla informatii despre tipurile sau proprietatile unei coloane din ResultSet.

· java.sql.Statement – un obiect de tip Statement este utilizat pentru realizarea unei interogari SQL statice si obtinerea rezultatelor produse ca urmare a executiei sale.

 

 

4.4. Conectarea la baza de date

 

Atunci cand dorim sa scriem sau sa citim date dintr-un fisier, prima operatiune care se realizeaza este deschiderea fisierului. Accesul la fisier este direct. In cazul fisierului care reprezinta baza de date avem nevoie de un strat intermediar care stie sa citeasca corect datele, dat fiind faptul ca ele sunt pastrate intr-un anumit format. Deci pentru citirea datelor din baza de date se utilizeaza un anumit protocol. Stratul intermediar care cunoaste acest protocol este reprezentat de driver. In concluzie contactul intre doua componente, aplicatia Java si baza de date, este realizat prin intermediul driver-ului.

Conectarea aplicatiei la baza de date se executa prin intermediul unui obiect de tip Connection. Pentru a obtine conexiunea trebuie sa furnizam adresa, sau altfel spus URL-ul respectivei baze de date. Acest URL reprezinta un mod de identificare a bazei da date in asa fel incat driverul corespunzator recunoaste denumirea si poate stabili o conexiune.

Conectarea aplicatiei la baza de date este o sarcina care revine in special clasei DriverManager. Aceasta este una dintre principalele clase ale pachetului java.sql. In momentul in care se apeleaza metoda getConnection(). Clasa DriverManager incearca sa gaseasca un driver care poate sa realizeze conexiunea cu respectiva baza de date. Aceasta clasa mentine o lista a tuturor driverelor inregistrate pe sistem si la cererea de conectare din partea unei aplicatii verifica raspunsul fiecarui driver din lista la url-ul transmis ca parametru. Schimbul de informatie dintre clasa DriverManager si celelalte drivere, are loc prin intermediul interfetei Driver, interfata pe care trebuie sa o implementeze fiecare driver prin metoda: getConnection(), clasa DriverManager apeleaza metoda connect() din cadrul interfetei Driver, metoda care realizeaza conexiunea reala cu baza de date.

URL-ul transmis ca si parametru la apelul metodei getConnection() contine un sir de caractere cu o semnificatie bine determinata: jdbc.odbc.WebData. Inainte de a studia semnificatia exacta a componentelor acestui URL sa vedem exact care este definitia URL-urilor.

Un URL – Uniform Resource Locator – reprezinta o modalitate de identificare a resurselor pe Internet. In general, atunci cand navigam pe Internet furnizam navigatorului o cale care specifica localizarea unui fisier sau a unui sistem legat la Internet, de exemplu https://java.sun.com/index.html. In alcatuirea unui URL se pot identifica doua parti importante: in primul rand este specificat protocolul utilizat pentru accesarea resursei iar mai apoi este furnizata adresa exacta a resursei. Adresa exacta a resursei poate cuprinde inclusiv numele site-ului (sistemului) pe care este localizat fisierul. Protocolul in cazul exemplului dat este http – Hyper Text Transfer Protocol – iar adresa exacta a resursei este data de java.sun.com/index.html.

In cazul URL-urilor JDBC, este vorba despre o cale de identificare a bazelor de date intr-un mod specific unui anumit driver. Astfel, la furnizarea URL-ului numai un anumit driver stie sa recunoasca URL-ul si sa decodifice informatiile furnizate in cadrul acestuia. Practic, cei care scriu driverele sunt cei care stabilesc modul in care va arata URL-ul JDBC care identifica driverul lor. Utilizatorii driverului nu trebuie sa-si faca probleme in acest sens: se va utiliza URL-ul furnizat odata cu driverul. Rolul JDBC este doar de a recomanda anumite conventii privind modul de alcatuire a unui URL. Ca urmare a faptului ca URL-urile JDBC pot fi utilizate cu un numar mare de drivere este normal ca structura lor sa fie foarte flexibila. In primul rand, URL-urile JDBC permit diferite scheme pentru denumirea bazelor de date. Apoi, URL-urile JDBC permit producatorilor de drivere sa inglobeze toate informatiile de care au nevoie. Aceasta permite aplicatiilor sa acceseze bazele de date fara ca utilizatorul sa fie nevoit sa recurga la actiuni de administrare a bazei de date. In al treilea rand, URL-urile JDBC permit specificarea unei denumiri logice pentru baza de date si pentru sistemul pe care este localizata baza de date. Maparea denumirii logice in denumirea fizica este realizata de un anumit serviciu de naming disponibil in cadrul retelei sau pe sistemul local.

Sintaxa standard pentru un URL JDBC este urmatoarea:

 
jdbc: < subprotocol > : < subname >

 

Se poate observa usor existenta celor trei parti ale unui URL JDBC, parti care au urmatoarea semnificatie:

  • jdbc reprezinta numele protocolului. In cadrul unui URL JDBC vom folosi intotdeauna protocolul jdbc.

  • subprotocolul poate reprezenta numele unui driver sau numele unui mecanism de conectare la baza de date. Un exemplu foarte sugestiv pentru denumirea unui subprotocol este odbc. Acest nume este rezervat pentru URL-urile care specifica surse de date de tipul ODBC. Pentru accesarea unei baze de date prin intermediul unui bridge JDBC-ODBC, cazul nostru este demonstrativ – jdbc:odbc:ProDb. In aceasta situatie, subprotocolul este odbc iar numele ProDb este denumirea unei surse de date ODBC locale.

Daca cineva doreste sa utilizeze un serviciu de naming atunci respectivul serviciu trebuie furnizat ca protocol. Utilizarea acestui serviciu este necesara atunci cand numele bazei de date nu reprezinta pe cel real. In acest caz URL-ul va arata astfel: jdbc:dnsnaming:ProDb. Aici numele subprotocolului este serviciul de naming DNS. Acest serviciu trebuie sa rezolve numele logic al bazei de date intr-un nume real care sa fie utilizat pentru conectarea la baza de date.

  • subname, este cea de-a treia componenta a URL-ului si reprezinta o modalitate de identificare a bazei de date. Sintaxa acestei componente poate varia in functie de driver si ca urmare poate contine toate informatiile necesare pentru localizarea bazei de date. In exemplul nostru ProDb este suficient pentru identificarea sursei de date pe sistemul local. Daca sursa de date se afla pe un alt sistem in reteaua locala sau chiar pe Internet atunci trebuie sa includem in cadrul URL-ului JDBC adresa respectivului sistem. Presupunand ca baza de date se gaseste pe un sistem aflat in Internet avand adresa www.utcluj.ro si numele subprotocolului utilizat pentru conectare este dbnet atunci URL-ul pentru conectarea la baza de date va avea forma: jdbc:dbnet://www.utcluj.ro:nrPort/WebData.

Revenind la exemplul nostru, obtinerea conexiunii are loc prin apelul metodei getConnection() a clasei DriveManager.

 

 
Connection con = DriveManager.getConnection(url, “ ”, “ ”);

 

La apelul metodei se transmit trei parametrii. Despre primul dintre acestia, URL-ul JDBC, am discutat mai sus. Urmatorii doi parametrii reprezinta numele utilizatorului care doreste accesarea bazei de date si respectiv parola asociata respectivului utilizator. In exemplul de mai sus acesti parametrii au valori nule pentru ca nu s-a cofigurat baza de date pentru a fi protejata.

 

Exemplu 1:

Utilizarea bridge-ului JDBC-ODBC:
private void getDBConnection() {
try{
Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");
Connection conn = DriverManager.getConnection( "jdbc:odbc:Profesori", "administrator", "asdf123");
stmt = conn.createStatement();
}
catch (Exception e){
System.out.println("Exceptie in metoda getConnection ");
}
}

 

Exemplu 2:

 

Utilizarea unui ORACLE Thin Driver in cazul unui client avand GUI de tip applet Java:
private void getConnection() {
try{
DriverManager.registerDriver(new OracleDriver());
conn=DriverManager.getConnection("jdbc:oracle:thin:@193.226.17.8:1521:orcl","scott", "tiger");
stmt = conn.createStatement();
}
catch(Exception s)
System.out.println ("Exceptie aparuta in metoda getConnection");
}
}

 

Exemplu 3

 

Utilizarea unui MySQL Driver
private void getConnection() {
try {
Class.forName("org.gjt.mm.mysql.Driver").newInstance();
String url = "jdbc:mysql://193.226.17.14/ProjectManagement";
String user = "adminManag";
String password = "parola";
Return
java.sql.DriverManager.getConnection(url,user,password);
}
catch (Exception e){
System.out.println ("Exceptie aparuta in metoda getConnection ");
}
}

4.5. Executarea comenzilor SQL

Odata obtinuta conectarea la baza de date avem la dispozitie un obiect de tip Connection. Acest obiect reprezinta conexiunea. O sesiune de conectare cu baza de date cuprinde toate enunturile SQL care sunt executate precum si rezultatele intoarse ca urmare a acestor prelucrari. O aplicatie poate avea una sau mai multe conexiuni cu baza de date.

Comanda SQL CREATE TABLE primeste ca parametru numele tabelei si denumirile campurilor care vor face parte din structura tabelei, in acest caz, Administrator(User, MMType).

Dupa cum am vazut, un obiect de tip Statement este creat cu ajutorul metodei create Statement() a clasei Connection. Pentru executarea efectiva a comenzii SQL, obiectul de tip Statement ne pune la dispozitie trei metode, fiecare cu o sarcina foarte precisa: execute(), executeQuery(), executeUpdate().

Metoda executeQuery() este utilizata pentru acele enunturi care produc un singur set de articole. Mai bine spus, cu ajutorul acestei metode se executa interogarile de tip SELECT.

Metoda executeUpdate() este utilizata pentru executarea enunturilor de tip INSERT, DELETE si UPDATE respectiv pentru enunturi de tip SQL DDL (data definition language) cum ar fi CREATE TABLE respectiv DROP TABLE. Enunturile SQL INSERT, DELETE si UPDATE au ca efect modificarea uneia sau mai multor coloane respectiv randuri. Metoda returneaza o valoare de tip intreg reprezentand numarul randurilor care au fost afectate. Pentru enunturi de tip CREATE TABLE sau DROP TABLE metoda executeUpdate returneaza intotdeauna o valoare nula.

Metoda execute() este utilizata atunci cand interogarea returneaza mai mult de un singur set de rezultate.

Dupa executarea unui enunt SQL, execute(), executeQuery sau executeUpdate() returneaza un rezultat. Imediat dupa obtinerea rezultatului, obiectul de tip Statement trebuie eliberat. Aceasta actiune este indeplinita de catre garbage colector. Cu toate acestea, este recomandat ca obiectul sa fie eliberat in mod explicit prin apelarea metodei close(). Astfel se elibereaza resursele necesare pentru baza de date si se evita problemele care ar putea aparea din cauza memoriei insuficiente.

Driverul JDBC realizeaza o conversie intre tipurile de date SQL si tipurile de date Java.

 

4.6. Conectarea la o baza de date Access

 

Exemplu – Clasa Java care realizeaza, in prima faza, o conexiune la o baza de date Microsoft Access iar apoi executa o serie de operatii care sunt observabile in momentul studierii amanuntite a codului.

 

 
package myPackage;
 
import java.sql.*;
import java.io.*;
 
public class Connex {
 
Connection conn;
Statement stmt;
String sqlQuery;
ResultSet result;
 
// realizarea conexiunii
public void getConnection() {
 
try{
Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");
 
Connection conn = DriverManager.getConnection("jdbc:odbc:NumeDB", "user", "password");
 
stmt = conn.createStatement ();
}
catch (Exception e){
System.out.println ("Exceptie aparuta in constructorul clasei JdbcTest");
}
}//end of Constructor
// citire ID user din tabelul Admin
public int getIdPass(String loginID, String parola){
int id=0;
String string = "select id from Admin where loginId='"+loginID+"' and password='"+parola+"'";
try{
result = stmt.executeQuery(string);
while(result.next()){
id=result.getInt(1);
System.out.println("M-am conectat cu id-ul "+id);
}
}
catch(Exception e){
System.out.println("Exceptie aparuta in metoda getLoginAdmin");
}
return id;
}//end of method
 
// citire coloana logID din tabelul Admin
public String getLoginAdmin(String logID){
String str= "";
try{
result = stmt.executeQuery("select "+logID+" from Admin");
while(result.next()){
str=result.getString(1);
System.out.println("Am citit din tabelul Admin "+str);
 
}
}
catch(Exception e){
System.out.println("Exceptie aparuta in metoda getLoginAdmin");
}
return str;
}//end of method
 
// inchidere conexiune
public void allClosing(){
try{
result.close();
stmt.close();
conn.close();
}
catch(Exception e){
System.out.println("Exceptie in metoda allClosing");
}
}
 
// citeste int dintr - un tabel
public int readIntTabel(String tabel, String coloana, String coloanaStiuta, String valoare){
int i=0;
String str = "SELECT "+coloana+" FROM "+tabel+" WHERE "+coloanaStiuta+" = '"+valoare+"'";
try{
 
result = stmt.executeQuery(str);
while (result.next()){
i = result.getInt(1);
}
}
catch (Exception e){
System.out.println("Exceptie in metoda citesteTabel");
}
return i;
}
 
//citeste String din tabel
public String citesteTabel(String tabel, String coloana, String coloanaStiuta, String valoare){
String str1="";
String str = "SELECT "+coloana+" FROM "+tabel+" WHERE "+coloanaStiuta+" = '"+valoare+"'";
try{
 
result = stmt.executeQuery(str);
while (result.next()){
str1 = result.getString(1);
}
}
catch (Exception e){
System.out.println("Exceptie in metoda citesteTabel");
}
return str1;
}
 
public String citesteTabel(String tabel, String coloana, String coloanaStiuta, int valoare){
String str1="";
String str = "SELECT "+coloana+" FROM "+tabel+" WHERE "+coloanaStiuta+" = "+valoare;
//System.out.println("Asta e stringul de interogare "+str);
try{
 
result = stmt.executeQuery(str);
while (result.next()){
str1 = result.getString(1);
}
}
catch (Exception e){
System.out.println("Exceptie in metoda citesteTabel");
}
return str1;
}
 
//stocare atribut in baza de date
public void scrieTabel(String tabel, String coloana, String valoare){
String str = "INSERT INTO "+tabel+" ("+coloana+") VALUES ('"+valoare+"');";
try{
if (getId(tabel, coloana, valoare)==0){
stmt.executeUpdate(str);
}
else{
System.out.println("Exista!");
}
}
catch (Exception e) {
System.out.println("Exceptie in metoda scrieTabel");
}
}
 
// executa UPDATE
public void updateTabel(String tabel, String coloana, String valoare, String coloanaStiuta, String valoareStiuta){
String str = "UPDATE "+tabel+" SET "+coloana+"='"+valoare+"' WHERE "+coloanaStiuta+"='"+valoareStiuta+"';";
try{
stmt.executeUpdate(str);
}
catch (Exception e) {
System.out.println("Exceptie in metoda updateTable");
}
}
 
// UPDATE
public void updateTabel(String tabel, String coloana, String valoare, String coloanaStiuta, int valoareStiuta){
String str = "UPDATE "+tabel+" SET "+coloana+"='"+valoare+"' WHERE "+coloanaStiuta+"="+valoareStiuta;
try{
stmt.executeUpdate(str);
}
catch (Exception e) {
System.out.println("Exceptie in metoda updateTable");
}
}