O aplicatie JDBC utilizeaza unul sau mai multe drivere din pachetul java.sql care sunt utilizate de catre clasa DriverManager. Driverele sunt specifice bazelor de date, deci pentru fiecare tip de baza de date se utilizeaza un driver special. In aceeasi aplicatie putem lucra cu baze de date diferite, deci implicit si cu mai multe drivere. Putem avea nevoie de un driver care comunica cu o baza de date Oracle aflata la distanta si de un altul care reprezinta legatura spre driverul ODBC local si comunica cu un server SQL. In aplicatii comunicatia cu o baza de date necesita urmatorii pasi:
Structura tabelei Inventory:
Nume_camp | Tip_camp | Lungime_camp |
NAME | Text | 40 |
QUANTITY | Numeric-Real | 20 |
Sursa Java:
import java.sql.*;Inainte sa executam programul precedent trebuie sa informam ODBC despre existenta unei surse de date care va fi Inventory.mdb. Sursa de date o vom denumi tot Inventory. Dupa ce s-a creat si s-a indexat baza de date se selecteaza din Control Panel--ODBC32. De aici se trece la pagina System DSN unde se introduce in caseta Data Source Name "Inventory". Dupa executarea acestor pasi tabela de date va fi atasata driverului permitand operatii cu aceasta. ODBC este un protocol care asigura accesul la baze de date utilizate sub s.o. Windows. In exemplul precedent aplicatia noastra acceseaza direct baza de date. Asemenea aplicatii se numesc aplicatii cu doua nivele. Intr-o abordare eleganta aplicatiile sunt proiectate pe trei nivele. In cazul acestor aplicatii clientul lucreaza cu un server care reprezinta un nivel intermediar intre client si baza de date.public class SimpleJDBC{
public static void main( String args[]){
try{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
String databaseName = "jdbc:odbc:Inventory";
Connection con =DriverManager.getConnection(databaseName,"username","password");
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("select * from Inventory");
while( rs.next()){
System.out.println(rs.getString(1)+":"+rs.getFloat(2));
}
}
catch( Exception e ){
e.printStackTrace();
}
}
}
Urmatorul exemplu va lucra cu o baza de date Access continand doua tabele.
Nume baza de date: Universitate.mdb
Nume tabel 1: Catedre
Structura:
CODCATEDRA Long
DENUMIRE
Text
Indexari: CODCATEDRA (cheie primara)
Nume tabel 2: Profesori
Structura:
CODPROFESOR Long
NUME
Text
CODCATEDRA Long
Indexari: CODPROFESOR (cheie primara); CODCATEDRA
import java.sql.*;public class SimpleJDBC{
public static void main( String args[]){
try{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
String databaseName = "jdbc:odbc:Universitate";
Connection con = DriverManager.getConnection(databaseName,"username","password");
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("select Profesori.codprofesor,Profesori.nume,Profesori.codcatedra,Catedre.nume from Catedre,Profesori where Catedre.codcatedra=Profesori.codcatedra order by catedre.nume");
while( rs.next()){
System.out.println(rs.getLong(1)+":"+rs.getString(2)+":"+rs.getLong(3)+":"+rs.getString(4));
}
}
catch( Exception e ){
e.printStackTrace();
}
}
}
Class.forName("com.sybase.jdbc.SybDriver");Prin apelul acestor metode se va incarca un driver JDBC. In clasa specifica driverului esista o metoda statica care va inregistra existenta sa cu DriverManager. Dupa incarcarea driverului trebuie creata legatura cu baza de date. Se ridica o serie de probleme:
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Statement stmt = con.createStatement();Metodele cele mai importante ale interfetei Statement sunt urmatoarele: executeQuery(String), executeUpdate(String), execute(String). Aceste metode se utilizeaza pentru executia codului SQL. Metoda executeQuery() executa comanda SQL si returneaza un obiect de tip ResultSet si se utilizeaza pentru executia comenziilor SQL de interogare SELECT. Metoda executeUpdate() executa comanda SQL primita ca parametru si returneaza numarul randurilor tabelei modificate (update count). Se utilizeaza pentru comenzile SQL de manipularea datelor: INSERT, UPDATE, DELETE si pentru comenzi de definire a datelor CREATE/DROP TABLE. In cazul comenzilor de definirea datelor valoarea returnata este 0. Ultima metoda execute() poate fi privita ca fiind generalizarea celorlaltor doua metode. Se utilizeaza daca comanda SQL poate returna deodata mai multe rezultate sau nu se cunoaste rezultatul executiei.
ResultSet rs = stmt.executeQuery("select * from Inventory");
while( rs.next()){Singurul pericol in asemenea constructii poate fi ca rezultatul interogarii nu produce nici o linie iar noi avem ceva asemanator cu constructia de mai jos:
System.out.println( rs.getString(1)+":"+rs.getFloat(2));
}
ResultSet rs = stmt.executeQuery("A Query...");Daca rezultatul interogarii este vid atunci constructia de mai sus va genera exceptie SQLException.
int i = rs.getInt(1);
|
|
|
CHAR | String | getString() |
VARCHAR | String | getString() |
LONGVARCHAR | String | getString() |
NUMERIC | java.math.BigDecimal | getBigDecimal() |
DECIMAL | java.math.BigDecimal | getBigDecimal() |
BIT | boolean | getBoolean() |
TINYINT | byte | getByte() |
SMALLINT | short | getShort() |
INTEGER | int | getInt() |
BIGINT | lonng | getLong() |
REAL | float | getFloat() |
DOUBLE | double | getDouble() |
BINARY | byte[] | getBytes() |
VARBINARY | byte[] | getBytes() |
LONGVARBINARY | byte[] | getBytes() |
DATE | java.sql.Date | getDate() |
TIME | java.sql.Time | getTime() |
TIMESTAMP | java.sql.Timestamp | getTimestamp() |
Clasa InventoryItem lucreaza cu o inregistrare din tabela Inventory.
Contine exact doua campuri, tipul acestor campuri coincide cu tipul campurilor
din tabela si contine metode de tip setXXX() care permit modificarea acestor
campuri precum si metode de tip getXXX() pentru returnarea valorilor din
aceste campuri.
Clasa InventoryManager va fi cea care contine logica aplicatiei. Constructorul
clasei incarca driverul pentru a realiza legatura cu baza de date, iar
celelalte metode permit operatiile necesare aplicatiilor si anume: ce cantitate
exista dintr-un anumit material, modificarea cantitatii dintr-o materie
si verificarea daca dintr-o anumita materie exista peste o anumita cantitate.
Diagrama de clase:
Sursa Java a aplicatiei:
Fisierul InventoryItem.java:
public class InventoryItem {Fisierul InventoryManager.java:
String item;
float amount;
public InventoryItem() {
item = "";
amount = 0;
}
public InventoryItem(String s, float a) {
item = s;
amount =a ;
}
public void setAmount(float a) {
amount = a;
}
public float getAmount() {
return amount;
}
public void setItem(String s) {
item = s;
}
public String getItem() {
return item;
}
}
import java.sql.*;Fisierul InventoryMain.java:
import java.util.Vector;
import java.util.Enumeration;public class InventoryManager {
Statement stmt;String databaseName = "jdbc:odbc:Inventory";
public InventoryManager() {
try {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
}
catch( Exception e ) {
e.printStackTrace();
}
}//Verifica daca din materia ingredient avem peste cantitatea amount
public boolean checkInventory(String ingredient, float amount){
try {
Connection con = DriverManager.getConnection(databaseName, "", "");
stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(
"SELECT Quantity FROM Inventory WHERE Ingredient = '"
+ ingredient +"'");
rs.next();
if (amount > rs.getFloat(1))
return false;
else
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
public boolean checkInventory(Vector ingredients) {
try {
Connection con = DriverManager.getConnection(databaseName, "", "");
stmt = con.createStatement();
Enumeration e = ingredients.elements();
while (e.hasMoreElements()) {
InventoryItem i = (InventoryItem) e.nextElement();
ResultSet rs = stmt.executeQuery(
"SELECT Quantity FROM Inventory WHERE Ingredient = '"
+ i.item + "'");
rs.next();
if (i.amount > rs.getFloat(1))
return false;
}
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}//Returneaza cantitatea din materia primita ca parametru
public float quantityOnHand(String ingredient)
throws SQLException {
Connection con =DriverManager.getConnection(databaseName, "", "");
stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT Quantity FROM Inventory WHERE Ingredient = '"
+ ingredient +"'");
rs.next();
return rs.getFloat(1);
}//Actualizeaza cantitatea din materia ingredient la cantitatea quantity
public void replenish(String ingredient, float quantity)
throws SQLException {
Connection con =
DriverManager.getConnection(databaseName, "", "");
stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(
"SELECT Quantity FROM Inventory WHERE Ingredient = '"
+ ingredient +"'");
rs.next();
stmt.executeUpdate(
"UPDATE Inventory SET Quantity = " +
(rs.getFloat(1) + quantity) +
" WHERE Ingredient = '" + ingredient +"'");
}}
public class InventoryMain {
public static void main(String args[]){
InventoryManager im = new InventoryManager();try {
System.out.println("Este cantitatea de zahar mai mare decat 90: " + im.checkInventory("zahar", 90f));
System.out.println("Cantitatea de zahar este:" + im.quantityOnHand("zahar"));
im.replenish("zahar", 100f);
System.out.println("Cantitatea de zahar este:" + im.quantityOnHand("zahar"));} catch (Exception e) {
e.printStackTrace();
}
}
}
In cazul aplicatiilor care lucreaza cu baze de date este foarte
frecventa utilizarea aceluiasi cod SQL de mai multe ori. RDBMS-urile faciliteaza
crearea instructiunilor preparate, care trebuie create, analizte si optimizate
o singura data in baza de date si dupa aceea pot fi utilizate in aplicatii.
JDBC faciliteaza utilizarea instructiunilor preparate prin clasa PreparedStatement.
Aceasta clasa este subclasa clasei Statement. Clasa PreparedStatement are
un constructor care primeste ca parametru un string reprezentand instructiunea
SQL.
PreparedStatement pstmt = con.preparedStatement("SELECT quantity FROM Inventory WHERE ingredient=?";Se poate observa ca instructiunea SQL contine si semne de intrebare. Aceste semne de intrebari reprezinta parametrii interogarii. Inainte de executia propriu-zisa a codului acesti parametrii pot fi setati. In exemplul precedent am utilizat o metoda checkInventory( Vector ingredients ) pentru a afla daca din fiecare materie prima avem cantitatile dorite.
Enumeration e =ingredients.elements();In acest exemplu pentru fiecare materie prima din vectorul ingredients s-a executat cate o instructiune SQL, timpul de executie astfel devenind foarte mare. Vom rescrie aceasta metoda utilizand un obiect de tip PreparedStatement facand ca aplicatia sa devina mai eficienta.
while( e.hasMoreElements()){
InventoryItem i = (InventoryItem) e.nextElement();
ResultSet rs = stmt.executeQuery( "SELECT quantity FROM Inventory WHERE ingredient =' " + i.item+" ' ");
rs.next();
if( rs.getFloat(1) < i.amount )
return false;
}
return true;
PreparedStatement pStmt = con.prepareStatement("SELECT Quantity
FROM Inventory WHERE Ingredient =?");
Enumeration e =ingredients.elements();
while( e.hasMoreElements()){
InventoryItem i = (InventoryItem) e.nextElement();
pStmt.setString(1,i.item);
ResultSet rs = stmt.executeQuery();
rs.next();
if( rs.getFloat(1) < i.amount )
return false;
}
return true;
Astfel codul SQL se compileaza o singura data pe partea de baza de date si la fiecare apel executeQuery() se va executa acel cod precompilat cu parametrii inlocuiti.Parametrii interogarii sunt setati de catre metodele setXXX() ale clasei ResultSet. Primul parametru al acestor metode este numarul de ordine al parametrului din secventa SQL care se inlocuieste, iar al doilea parametru este valoarea cu care se inlocuieste.
Tip Java | Tip SQL | Metoda |
Java.math.BigDecimal | NUMERIC | setBigDecimal() |
boolean | BIT | setBoolean() |
byte | TINYINT | setByte() |
short | SMALLINT | setShort() |
int | INTEGER | setInt() |
long | BIGINT | setLong() |
float | REAL | setFloat() |
double | DOUBLE | setDouble() |
byte[] | VARBINARY sau LONGVARBINARY | setBytes() |
java.sql.Date | DATE | setDate() |
java.sql.Time | TIME | setTime() |
java.sql.Timestamp | TIMESTAMP | setTIMESTAMP() |
String | VARCHAR sau LONGVARCHAR | setString() |
|
|
COD | Numeric(10) |
NUME | Text(30) |
IMAGINE | OLE Object |
Dupa crearea bazei de date sursa de date trebuie sa fie facuta public
prin intermediul ODBC.
Datele in aceasta tabela vor fi introduse de catre program.
Nume clasa: JDBCImages
Date:
-databaseName: String
-con: Connection
-pictures: String[5]
-pStmt1, pStmt2: PreparedStatement
Metode:
+JDBCImages()
+writeRecord( int cod )
+readRecord( int cod, String fileName)
Sursa Java:
import java.io.*;
import java.sql.*;public class JDBCImages{
String databaseName = "jdbc:odbc:Imagini";
Connection con = null;
String pictures[]={"curs2_fig1.gif","curs2_fig2.gif",
"curs2_fig3.gif","curs2_fig4.gif", "curs2_fig5.gif"};
PreparedStatement pStmt1 = null;In exemplul precedent constructorul incarca driverul pentru baza de date si realizeaza conexiunea cu aceasta, dupa care creaza doua instructiuni preparate: una pentru a insera o inregistrare in baza de date si una pentru a obtine campul imagine din baza de date. Prima instructiune preparata va fi utilizata de catre metoda writeRecord( int ), care primind indicele unui element din tabloul pictures insereaza in baza de date o inregistrare. A doua instructiune preparata este folosita de catre readRecord( int, String ). Aici primul parametru indica codul inregistrarii care trebuie selectata, iar al doilea parametru va fi utilizat pentru numele fisierului in care se salveaza datele obtinute din baza de date.Pentru a scrie intr-un asemenea camp sau a citi din aceasta se vor utiliza metodele setBinaryStream() respectiv getBinaryStream() ale clasei ResultSet.
PreparedStatement pStmt2 = null;public JDBCImages(){
// Incarcare driver - Creare conexiune
try{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
con = DriverManager.getConnection(databaseName,"","");
pStmt1 = con.prepareStatement("INSERT INTO imagini
(cod,nume,imagine) VALUES (?,?,?)");
pStmt2 = con.prepareStatement("SELECT imagine FROM
imagini WHERE cod=?");}
catch( Exception e ){
System.out.println("Eroare constructor");
e.printStackTrace();
}
}public void writeRecord( int cod )
{
try{File inFile = new File( pictures[ cod ] );
int flength = (int) inFile.length();
FileInputStream in = new FileInputStream( inFile );
pStmt1.setInt(1,cod);
pStmt1.setString(2,pictures[cod]);
pStmt1.setBinaryStream(3,in,flength);
pStmt1.executeUpdate();
}
catch( Exception e ){
System.out.println("Eroare INSERT");
e.printStackTrace();
}
}public void readRecord( int cod, String fileName )
{byte [] picture = new byte[ 1024 ];
try{pStmt2.setInt(1,cod);
File outFile = new File(fileName);
FileOutputStream out = new FileOutputStream( outFile );
ResultSet rs = pStmt2.executeQuery();
rs.next();
InputStream ins = rs.getBinaryStream(1);
int n;
int s =0;
while( (n=ins.read(picture))>0)
{
out.write( picture,0,n);
s+=n;
}
System.out.println("Total bytes read: "+Integer.toString(s));
out.close();
}
catch( Exception e ){
System.out.println("eroare SELECT");
e.printStackTrace();
}
}public static void main( String args[]){
int i;
JDBCImages o = new JDBCImages();
o.writeRecord( 1 );
o.readRecord( 1, "picture1.gif");
}
}
Statement stmt = con.createStatement( ResultSet.TYPE_SCROLL_SENSITIVE,
ResultSet.CONCUR_READ_ONLY);
Intr-un ResultSet derulabil putem utiliza metodele:
Metoda | Rezultat |
boolean first() | Pozitionarea indicatorului pe prima linie |
boolean previous() | Pozitionarea indicatorului pe linia precedenta |
boolean next() | Pozitionarea indicatorului pe prima urmatoare |
boolean last() | Pozitionarea indicatorului pe ultima linie |
boolean absolute( int poz ) | Pozitionarea indicatorului pe poztia indicata |
boolean relative ( int pozitie_relativa ) | Pozitionarea indicatorului relativ la pozitia curenta |
//Creare Statement
Statement stmt = con.CreateStatement();
//Adaugare comenzi
stmt.addBatch("INSERT INTO Catedre (CodCatedra, Denumire)
VALUES (5,'Stiinte Economice')");
stmt.addBatch("INSERT INTO Profesori (CodProfesor, Codcatedra,
Nume ) VALUES (10,2,'Pop Ioan')");
//Executarea comenzilor
int [] updateCounts = stmt.executeBatch();
Metoda executeBatch() returneaza un tablou de elemente, continand randurile care au fost afectate prin comanda data. Comenzile dintr-un batch se executa in ordinea in care au fost asezate in batch. Daca vreo comanda este incorecta atunci se genereaza exceptia BatchUpdateException si se opreste executarea comenzilor din batch. Batch-urile pot fi utilizate si in cazul instructiunilor preparate.
PreparedStatement pstmt = con.prepareStatement("DELETE FROM
Catedre WHERE Denumire=?");
Enumeration e =v.elements();
while( e.hasMoreElements()){
pstmt.setString( 1, {String) e.nextElement());
pstmt.addBatch();
}
int [] updateCounts = pstmt.executeBatch();
1. Sa scrie o aplicatie care sa permita interogarea oricarei baze
de date. Rezultatele selectiei se vor afisa intr-un element de control
TextArea. Interfata aplicatiei sa aiba urmatoarea forma:
Sursa Java:
import java.awt.*;
import java.awt.event.*;
import java.sql.*;
public class TestJdbc extends Frame implements ActionListener{
String Driver=null;
String DataBase = null;
String UserName = null;
String Password
= null;
String SQL= null;
TextField tfDriver;
TextField tfDataBase;
TextField tfUserName;
TextField tfPassword;
TextField tfSQL;
Button b;
TextArea ta;
// Constructorul creaza
interfata aplicatiei si inregistreaza receptorii
// evenimentelor la
sursele acestora
public TestJdbc(){
setTitle("Jdbc Application");
setLayout( new FlowLayout());
Label l1 = new Label("Driver");
add( l1 );
tfDriver =new TextField(60);
add(tfDriver);
Label l2 = new Label("DataBase URL");
add( l2 );
tfDataBase =new TextField(60);
add(tfDataBase);
Label l3 = new Label("User name:");
add( l3 );
tfUserName =new TextField(60);
add(tfUserName);
Label l4 = new Label("Password:");
add( l4 );
tfPassword =new TextField(60);
add(tfPassword);
Label l5 = new Label("SQL:");
add( l5 );
tfSQL =new TextField(60);
add(tfSQL);
b= new Button( "Executa");
add(b);
b.addActionListener( this);
ta= new TextArea( 10,60);
add(ta);
addWindowListener( new MyWindowAdapter());
}
//Prelucrarea datelor din formularul prezentat in interfata aplicatiei
private void processData(){
Driver = tfDriver.getText();
if( Driver.equals(""))
Driver="sun.jdbc.odbc.JdbcOdbcDriver";
DataBase =tfDataBase.getText();
if( DataBase.equals(""))
DataBase="jdbc:odbc:Demo";
UserName = tfUserName.getText();
Password = tfPassword.getText();
SQL = tfSQL.getText();
if ( SQL.equals(""))
SQL="Select * From Catedre";
}
// Tratarea evenimentului
generat prin apasarea butonului "Executa"
// Se incarca driverul
pt. baza de date
// Se creaza conexiunea
cu baza de date
// Se creaza obiectul
de tip Statement, care va executa codul SQL
//
public void actionPerformed(
ActionEvent e ){
ta.setText("");
processData();
try{
Class.forName( Driver );
Connection con =
DriverManager.getConnection(DataBase,UserName,Password);
Statement stmt= con.createStatement();
// Se obtin rezultatele selectiei
ResultSet rs = stmt.executeQuery( SQL );
// Informatii (metadatele) despre selectie
// Se poate obtine atat numarul coloanelor
// din selectie, cat si tipurile acestor
// coloane
ResultSetMetaData rsmd = rs.getMetaData();
// Prelucrarea rezultatelor
int numCols = rsmd.getColumnCount();
while( rs.next() )
{
String record="";
for (int i=1; i<=numCols; i++)
{
int dataType = rsmd.getColumnType( i );
record=record+getField(rs,dataType, i)+" ";
}
ta.append( record+"\n" );
}
}
catch( SQLException ex1 ){
System.out.println(ex1.toString());
}
catch( ClassNotFoundException ex2 ){
System.out.println(ex2.toString());
}
}
private String getField(ResultSet rs, int
dataType, int col)
throws SQLException
{
switch(dataType) {
case Types.DATE:
java.sql.Date date = rs.getDate(col);
return date.toString();
case Types.TIME:
java.sql.Time time = rs.getTime(col);
return time.toString();
case Types.TIMESTAMP:
java.sql.Timestamp timestamp
= rs.getTimestamp(col);
return timestamp.toString();
case Types.CHAR:
case Types.VARCHAR:
case Types.LONGVARCHAR:
String str = rs.getString(col);
return str;
case Types.NUMERIC:
case Types.DECIMAL:
java.math.BigDecimal numeric
= rs.getBigDecimal(col, 10);
return numeric.toString();
case Types.BIT:
boolean bit = rs.getBoolean(col);
return (new Boolean(bit)).toString();
case Types.TINYINT:
byte tinyint = rs.getByte(col);
return (new Integer(tinyint)).toString();
case Types.SMALLINT:
short smallint = rs.getShort(col);
return (new Integer(smallint)).toString();
case Types.INTEGER:
int integer = rs.getInt(col);
return (new Integer(integer)).toString();
case Types.BIGINT:
long bigint = rs.getLong(col);
return (new Long(bigint)).toString();
case Types.REAL:
float real = rs.getFloat(col);
return (new Float(real)).toString();
case Types.FLOAT:
case Types.DOUBLE:
double longreal = rs.getDouble(col);
return (new Double(longreal)).toString();
case Types.BINARY:
case Types.VARBINARY:
case Types.LONGVARBINARY:
byte[] binary = rs.getBytes(col);
return new String(binary);
}
return "";
}
// Pt. tratarea evenimentului
WindowClosing
class
MyWindowAdapter extends WindowAdapter{
public void windowClosing( WindowEvent e ){
System.exit(0);
}
}
public static void
main( String args[]){
TestJdbc frame = new TestJdbc();
frame.setBounds(1,1,300,300);
frame.setVisible( true );
}
}