Sunteți pe pagina 1din 12

1

JDBC
JDBC (Java DataBase Connectivity) este o parte a Java API dedicată
conectării la baze de date. Utilizând JDBC, putem executa instrucţiuni SQL pentru
orice bază de date relaţională.
Etapele principale într-o aplicaţie ce foloseşte JDBC sunt următoarele:
- deschiderea unei conexiuni către baza de date;
- crearea unui "obiect instrucţiune" prin care transmitem instrucţiuni SQL către
gestionarul bazei de date;
- regăsirea rezultatelor.

Principalele clase şi interfeţe se găsesc în pachetul java.sql. Dintre acestea


vom prezenta doar următoarele interfeţe:
public interface Connection
stabileşte conexiunea la baza de date;
public interface Statement
crează un obiect instrucţiune ce conţine o instrucţiune SQL şi îl transmite bazei
de date;
public interface ResultSet
permite regăsirea rezultatelor interogării;
public interface PreparedStatement
permite lucrul cu instrucţiuni SQL cu parametri.

Vom prezenta în continuare numai principalele aspecte legate de JDBC.


Pentru a putea ajunge la o aplicaţie efectivă, presupunem că cititorul este
familiarizat cu forma instrucţiunilor SQL şi că baza de date este creată folosind
Microsoft Access. De asemenea, presupunem pentru moment că baza de date este
locală.

Crearea bazei de date cu Microsoft Access

Fie Agenda numele bazei de date, în care folosim tabelul Persoane.


Secvenţa standard de acţiuni ce trebuie întreprinsă este următoarea:
- deschid Microsoft Access
- Blank Access Database
- merg în directorul meu
- File Name: Agenda
- Create
- selectez: Create Table in Design view şi Tables
- Open
- introduc numele câmpurilor + tipul lor
- închid
- Salvez sub numele: Persoane
- fără cheie primară
- Click pe Persoane: pot introduce înregistrări
- închid toate ferestrele.
2

Pentru exemplificare, plecăm de la următorul conţinut al tabelei Persoane:

Înregistrarea bazei în driver-ul ODBC

Baza de date astfel creată este însă particulară şi nu este recunoscută ca atare
de orice sistem. De aceea trebuie folosit un "intermediar" care să permită accesarea şi
exploatarea ei. Un astfel de intermediar este ODBC (Open DataBase Connectivity).
Baza de date trebuie conectată la o sursă de baze de date ODBC, prin înregistrarea ei
la driver-ul ODBC.
Adresa sursei de date se precizează prin:
String url="jdbc:odbc:sursă_date"
Presupunem că numele sursei de date este AgendaODBC. Mergem succesiv în:
- Administrative Tools
- Data Sources (ODBC)
- System DSN sau UserDSN (baza de date a sistemului sau a utilizatorului)
- Add
- selectez *.mdb
- Data Source Name: AgendaODBC
- Select
- merg în directorul meu
- selectez Agenda.mdb
- OK
- OK
- închid.
Încărcarea driver-ului ODBC se realizează prin:
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

Conectarea la baza de date

Conectarea la baza de date se realizează prin crearea unui obiect de tipul


Connection (interfaţă care încearcă să realizeze o conexiune la baza de date asociată
sursei de date ODBC). Modalitatea tipică de conectare este următoarea:
3

try {
String url="jdbc:odbc:AgendaODBC";
Connection con =
DriverManager.getConnection(url,"user","parola");
}
catch (SQLException e) {...}
Se observă că trebuie trimis numele utilizatorului şi o parolă, pentru o
eventuală validare a accesului la baza de date.

Executarea de instrucţiuni SQL

Pentru executarea de instrucţiuni SQL trebui mai întâi creat un "obiect


instrucţiune", adică un obiect de tipul interfeţei Statement:
Statement stmt = con.createStatement();
unde createStatement este o metodă a interfeţei Connection. pe baza acestui
obiect este posibilă executarea de instrucţiuni SQL, cu primirea rezultatelor căutării.
În continuare trebuie creat un şir de caractere care să reprezinte o comandă
SQL validă:
String sql = "...";

Urmează transmiterea cererii şi obţinerea rezultatului interogării folosind una


dintre metodele:
public ResultSet executeQuery(String sql) throws SQLException
unde sql reprezintă o instrucţiune SQL de tip SELECT. Este întors un obiect de
tipul ResultSet care permite regăsirea rezultatelor cererii;
public int executeUpdate(String sql) throws SQLException
unde sql reprezintă o instrucţiune SQL de unul dintre tipurile INSERT,
UPDATE, sau DELETE. Este întors numărul liniei (înregistrării) afectate.

Interfaţa:
public interface ResultSet
permite regăsirea, sub forma unui tabel, a rezultatelor căutării la executarea unei
metode executeQuery. Un obiect de acest tip menţine un cursor la linia curentă din
tabel, iniţial la prima linie a acestuia. Dintre metodele interfeţei ResultSet descriem
deocamdată numai următoarele:
public boolean next()
întoarce true dacă şi numai dacă mai există linii în rs, caz în care se trece la
următoarea înregistrare;
public gettip(String s)
întoarce conţinutul câmpului cu numele s din înregistrarea curentă ca o valoare
de tipul tip din Java; tip poate fi String, Int etc.

Observaţie. Obiectele con şi stmt trebuie "închise" în final prin invocarea


metodei close().

Exemplu
4

Înregistrările (liniile) din baza de date cu care lucrăm corespund informaţiilor


despre o persoană şi au câmpurile Nume, Prenume, Localitate şi Salariu, cu
semnificaţii evidente. Corespunzător, scriem clasa Persoana în care constructorul
iniţializează câmpurile, iar metodele întorc valorile lor.
class Persoana {
private String nume,prenume,localitate;
private int salariu;
public Persoana(String n, String p, String l, int s) {
nume=n; prenume=p; localitate=l; salariu=s;
}
public String rNume() { return nume; }
public String rPrenume() { return prenume; }
public String rLocalitate() { return localitate; }
public int rSalariu() { return salariu; }
public String toString() {
return "\t"+ nume+"\t"+prenume+"\t"+
localitate+"\t"+salariu;
}
}

Asupra bazei de date vom executa doar două operaţii:


- selectarea tuturor înregistrărilor cu un nume şi o localitate date:
SELECT * FROM PERSOANE WHERE NUME='...' AND LOCALITATE='...'
pentru care vom folosi metoda executeQuery;
- inserarea unei noi înregistrări:
INSERT INTO PERSOANE (NUME,PRENUME,LOCALITATE,SALARIU)
VALUES('...','...','...','...')
pentru care vom folosi metoda executeUpdate.
Aceste operaţii corespund metodelor cauta şi adauga din clasa BD, clasa
care are un câmp stmt de tipul Statement, care primeşte valoare prin constructor:
import java.sql.*;
class BD {
Statement stmt;
BD(Statement s) { stmt = s; }
public void cauta (String nume, String loc)
throws Exception {
Persoana p = null;
String my_query = "SELECT * FROM PERSOANE WHERE NUME='" +
nume + "' AND LOCALITATE='" + loc + "'";
ResultSet rs = stmt.executeQuery(my_query);
while ( rs.next() ) {
p = new Persoana(rs.getString("Nume"),
rs.getString("Prenume"),
rs.getString("Localitate"),
rs.getInt("Salariu") );
IO.writeln(""+p);
}
}
public void adauga(Persoana p) throws Exception {
String my_stmt =
5

"INSERT INTO PERSOANE (NUME,PRENUME,LOCALITATE,SALARIU)"


+ " VALUES('" + p.rNume() + "','" + p.rPrenume() + "','"
+ p.rLocalitate()+"','" + p.rSalariu() + "');";
int i = stmt.executeUpdate(my_stmt);
IO.writeln(""+p);
}
}

Clasa Agenda de mai jos conţine numai metoda principală. Această metodă
prevede crearea unei conexiuni con la baza de date, a unui "obiect instrucţiune" stmt
şi a unui obiect bd de tipul BD. În continuare sunt citite de la intrare şiruri de caractere
şi se întreprind următoarele acţiuni:
- dacă primul şir de caractere citit este "INSERT", mai sunt citite 3 şiruri de
caractere şi un număr întreg; prin invocarea metodei adauga a clasei BD este
inserată o nouă înregistrare în baza de date;
- dacă primul şir de caractere citit este "SELECT", atunci mai sunt citite două şiruri
de caractere nume şi loc şi este invocată metoda cauta a clasei BD pentru listarea
tuturor personelor cu numele nume şi localitatea loc;
- dacă primul şir de caractere citit este "STOP", atunci programul se termină.
import java.sql.*;
class Agenda {
public static void main(String[] args) throws Exception {
String id,nume,prenume,loc; int sal; Persoana P;
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
String url = "jdbc:odbc:AgendaODBC";
Connection con =
DriverManager.getConnection(url,"Vasile","Sile");
Statement stmt = con.createStatement();
BD bd = new BD(stmt);
for ( ; ; ) {
IO.writeln("*** Comanda noua : ***");
id = IO.readString();
if (id.equals("STOP")) break;
else if (id.equals("SELECT")) {
nume = IO.readString(); loc = IO.readString();
bd.cauta(nume,loc);
}
else if (id.equals("INSERT")) {
nume = IO.readString(); prenume = IO.readString();
loc = IO.readString(); sal = (int) IO.read();
P = new Persoana(nume,prenume,loc,sal);
bd.adauga(P);
}
else IO.writeln("Comanda incorecta");
}
con.close(); stmt.close();
}
}

De exemplu, dacă vom introduce:


"SELECT" "Ionescu" "Sibiu"
6

va fi afişat:
Ionescu Ion Sibiu 510
Ionescu Vasile Sibiu 560
Ionescu Dan Sibiu 460

Accesarea unei baze de date aflate la distanţă folosind socket-uri

Deoarece se vor trimite la distanţă obiecte de tipul Persoana, aceasta trebuie


să implementeze interfaţa Serializable:

import java.io.*;
class Persoana implements Serializable {
private String nume,prenume,localitate;
private int salariu;
public Persoana(String n, String p, String l, int s) {
nume=n; prenume=p; localitate=l; salariu=s;
}
public String rNume() { return nume; }
public String rPrenume() { return prenume; }
public String rLocalitate() { return localitate; }
public int rSalariu() { return salariu; }
public String toString() {
return "\t"+ nume+"\t"+prenume+"\t"+
localitate+"\t"+salariu;
}
}

Serverul va folosi clasa Server_BD.java:

import java.io.*; import java.sql.*;


import java.util.*; import java.net.*;
class Server_BD {
public static void main(String[] sss) throws Exception {
ServerSocket ss = new ServerSocket(7777);
Socket cs;
IO.writeln("Serverul a pornit");
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
String url = "jdbc:odbc:AgendaODBC";
Connection con =
DriverManager.getConnection(url,"Vasile","Sile");
Statement stmt = con.createStatement();
IO.writeln("S-a creat legatura la baza de date");
while (true) {
cs = ss.accept();
IO.writeln("Client nou");
new Conexiune(cs,stmt);
}
}
}

class Conexiune extends Thread {


7

Socket cs; Statement stmt;


Persoana p; String id,nume,prenume,loc; int sal;
Vector v; DataInputStream dis; ObjectOutputStream oos;
Conexiune(Socket client, Statement s) {
cs = client; stmt = s; start();
}
public void run() {
try {
dis = new DataInputStream( cs.getInputStream());
oos = new ObjectOutputStream( cs.getOutputStream());
String sql;
for( ; ; ) {
id = dis.readUTF();
if(id.equals("SELECT")) {
v = new Vector();
nume = dis.readUTF(); loc = dis.readUTF();
sql = "SELECT * FROM PERSOANE WHERE NUME='" +
nume + "' AND LOCALITATE='" + loc + "'";
IO.writeln("Comanda: " + sql);
ResultSet rs = stmt.executeQuery(sql);
while ( rs.next() ) {
p = new Persoana(rs.getString("Nume"),
rs.getString("Prenume"),
rs.getString("Localitate"),
rs.getInt("Salariu") );
IO.writeln(""+p); v.addElement(p);
}
oos.writeObject(v);
}
else {
// comanda INSERT
nume = dis.readUTF(); prenume = dis.readUTF();
loc = dis.readUTF(); sal = dis.readInt();
p = new Persoana(nume, prenume, loc, sal);
IO.writeln(p+"");
sql = "INSERT INTO PERSOANE "+
"(NUME,PRENUME,LOCALITATE,SALARIU)" +
" VALUES('" + p.rNume() + "','" +
p.rPrenume() + "','" + p.rLocalitate() +
"','" + p.rSalariu() + "');";
IO.writeln("Comanda: " + sql);
stmt.executeUpdate(sql);
IO.writeln("S-a adaugat: "+p);
}
}
}
catch(IOException e) { }
catch(SQLException e) { }
}
}

Clienţii vor folosi clasa Client_BD.java:


8

import java.io.*; import java.net.*; import java.util.*;


class Client_BD {
public static void main(String[] args) throws Exception {
String id; Persoana P; Vector v;
Socket cs = new Socket("localhost",7777);
DataOutputStream dos =
new DataOutputStream(cs.getOutputStream());
ObjectInputStream ois =
new ObjectInputStream(cs.getInputStream());
for ( ; ; ) {
IO.writeln("*** Comanda noua : ***");
id = IO.readString();
if (id.equals("STOP")) break;
else if (id.equals("SELECT")) {
dos.writeUTF("SELECT");
dos.writeUTF(IO.readString());
dos.writeUTF(IO.readString());
v = (Vector) ois.readObject();
while ( !v.isEmpty() ) {
P = (Persoana) v.elementAt(0);
IO.writeln(""+P); v.removeElementAt(0);
}
}
else if (id.equals("INSERT")) {
dos.writeUTF("INSERT");
dos.writeUTF(IO.readString());
dos.writeUTF(IO.readString());
dos.writeUTF(IO.readString());
dos.writeInt( (int) IO.read());
}
else IO.writeln("Comanda incorecta");
}
}
}

Accesarea unei baze de date aflate la distanţă folosind RMI

Java oferă multe posibilităţi de a rezolva această problemă. Dintre acestea


alegem mecanismul invocării la distanţă (RMI). Practic, vom scrie un server de Web
ce va funcţiona pe maşina pe care se află baza de date.
Vom descrie pas cu pas modificările ce trebuie aduse programului de mai sus.
Deoarece se transmit la distanţă obiecte de tipul clasei Persoana, aceasta
trebuie să fie serializabilă; ea se află atât pe client, cât şi pe server.

import java.io.*;
class Persoana implements Serializable {
private String nume,prenume,localitate; int salariu;
public Persoana(String n, String p, String l, int s) {
nume=n; prenume=p; localitate=l; salariu=s;
9

}
public String rNume() { return nume; }
public String rPrenume() { return prenume; }
public String rLocalitate() { return localitate; }
public int rSalariu() { return salariu; }
public String toString() {
return "\t"+ nume+"\t"+prenume+
"\t"+localitate+"\t"+salariu;
}
}

Tot atât pe client cât şi pe server se va afla interfaţa I_BD:


import java.rmi.*; import java.sql.*; import java.util.*;
interface I_BD extends Remote {
public Vector cauta (String nume, String loc)
throws Exception;
public void adauga(Persoana p) throws Exception;
}
unde metoda cauta întoarce într-un vector înregistrările de tip Persoana selectate.

Clientul foloseşte clasa Client_BD_RMI:


import java.sql.*; import java.rmi.*; import java.util.*;
class Client_BD_RMI {
public static void main(String[] args) throws Exception {
String id,nume,prenume,loc; int sal;
Persoana P; Vector v;
I_BD bd = (I_BD) Naming.lookup("rmi:///bd");
for ( ; ; ) {
IO.writeln("*** Comanda noua : ***");
id = IO.readString();
if (id.equals("STOP")) break;
else if (id.equals("SELECT")) {
nume = IO.readString(); loc = IO.readString();
v = bd.cauta(nume,loc);
while ( !v.isEmpty() ) {
P = (Persoana) v.elementAt(0);
IO.writeln(""+P); v.removeElementAt(0);
}
}
else if (id.equals("INSERT")) {
nume = IO.readString(); prenume = IO.readString();
loc = IO.readString(); sal = (int) IO.read();
P = new Persoana(nume,prenume,loc,sal);
bd.adauga(P);
}
else IO.writeln("Comanda incorecta");
}
}
}

Serverul foloseşte clasa Server_BD_RMI:


10

import java.sql.*; import java.util.*;


import java.rmi.*; import java.rmi.server.*;
class Server_BD_RMI extends UnicastRemoteObject
implements I_BD {
Statement stmt;
Server_BD_RMI(Statement s) throws Exception { stmt=s; }
public Vector cauta(String nume, String loc)
throws Exception {
Persoana p = null; Vector v = new Vector();
String sql = "SELECT * FROM PERSOANE WHERE NUME='" +
nume + "' AND LOCALITATE='" + loc + "'";
IO.writeln("Comanda: " + sql);
ResultSet rs = stmt.executeQuery(sql);
while ( rs.next() ) {
p = new Persoana(rs.getString("Nume"),
rs.getString("Prenume"),
rs.getString("Localitate"),
rs.getInt("Salariu") );
IO.writeln(""+p); v.addElement(p);
}
return v;
}
public void adauga(Persoana p) throws Exception {
String sql =
"INSERT INTO PERSOANE (NUME,PRENUME,LOCALITATE,SALARIU)"
+ " VALUES('" + p.rNume() + "','" + p.rPrenume() + "','"
+ p.rLocalitate()+"','" + p.rSalariu() + "');";
IO.writeln("Comanda: " + sql);
stmt.executeUpdate(sql);
IO.writeln("S-a adaugat: "+p);
}
public static void main(String[] args) throws Exception {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
String url = "jdbc:odbc:AgendaODBC";
Connection con =
DriverManager.getConnection(url,"Vasile","Sile");
Statement stmt = con.createStatement();
Server_BD_RMI bd = new Server_BD_RMI(stmt);
Naming.rebind("rmi:///bd",bd);
}
}

Instrucţiuni SQL cu parametri

Uneori programul trimite cereri de aceleaşi tip, dar cu alte valori pentru unul
sau mai multe câmpuri. De exemplu putem trimite cereri multiple în care doar
valoarea unui câmp (unor câmpuri) diferă. În aceste situaţii este utilă, dar şi eficientă,
folosirea unor instrucţiuni SQL în care apar parametri. Concret, în locul câmpului
(câmpurilor) vom introduce caracterul '?', umând ca acesta să fie înlocuit cu o
11

valoare efectivă înainte de cererea propriu-zisă. Eficienţa derivă din faptul că


instrucţiunile parametrizate sunt precompilate.
Pentru exemplul considerat mai sus putem forma cererea parametrizată:
SELECT * FROM PERSOANE WHERE SALARIU>?
urmând ca înainte de fiecare cerere să înlocuim câmpul marcat prin '?' cu o valoare
efectivă printr-o metodă de setare (vezi mai jos).

Gestiunea instrucţiunilor parametrizate este facilitată de interfaţa:


public interface PreparedStatement extends Statement
din pachetul java.sql. Interfaţa defineşte un obiect ce reprezintă o instrucţiune SQL
precompilată. Obiectul poate fi folosit eficient pentru a executa în mod repetat
instrucţiunea, dar pentru seturi diferite de parametri.
Descriem în continuare principalele metode ale acestei interfeţe.
public ResultSet executeQuery() throws SQLException
este analoaga metodei cu acelaşi nume din interfaţa Statement.
public int executeUpdate() throws SQLException
este analoaga metodei cu acelaşi nume din interfaţa Statement.

Metodele de setare:
 public void setTip(int i, tip t) throws SQLException
setează parametrul cu numărul de ordine i, cu precizarea că numerotarea începe
cu 1. Driver-ul îl converteşte la o valoare de tipul tipSQL şi îl trimite bazei de
date. Iată câteva cazuri particulare:

Tip tip tipSQL


Boolean boolean BIT
Byte byte TINYINT
Short short SMALLINT
Int int INTEGER
Long long BIGINT
Float float FLOAT
Double double DOUBLE
BigDecimal BigDecimal NUMERIC
String String VARCHAR
Date Date DATE
Time Time TIME
Ref Ref REF
URL URL DATALINK

Exemplu. Reluăm exemplul anterior şi urmărim listarea succesivă a tuturor


înregistrărilor în care salariul este mai mare decât 450, 500, 550, 600.

import java.sql.*;
class Agenda1 {
public static void main(String[] args) throws Exception {
String id,nume,prenume,loc; int sal; Persoana P;
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
String url = "jdbc:odbc:AgendaODBC";
Connection con =
DriverManager.getConnection(url,"Vasile","Sile");
12

Persoana p;
String cerere="SELECT * FROM PERSOANE WHERE SALARIU>?";
PreparedStatement pstmt = con.prepareStatement(cerere);
for (int i=400; i<650; i=i+50) {
pstmt.setInt(1,i);
IO.writeln("Persoane cu salariul > "+i);
ResultSet rs = pstmt.executeQuery();
while( rs.next() ) {
p = new Persoana( rs.getString("Nume"),
rs.getString("Prenume"),
rs.getString("Localitate"),
rs.getInt("Salariu")
);
IO.writeln(""+p);
}
}
con.close(); pstmt.close();
}
}

Executarea programului conduce la afişarea următoarelor rezultate:


Persoane cu salariul > 450
Ionescu Ion Sibiu 510
Ionescu Vasile Sibiu 560
Vasile Vasile Cluj 610
Ionescu Dan Sibiu 460
Persoane cu salariul > 500
Ionescu Ion Sibiu 510
Ionescu Vasile Sibiu 560
Vasile Vasile Cluj 610
Persoane cu salariul > 550
Ionescu Vasile Sibiu 560
Vasile Vasile Cluj 610
Persoane cu salariul > 600
Vasile Vasile Cluj 610

S-ar putea să vă placă și