Accesul bazelor de date din Java.

JDBC-Java Database Connectivity

  1. Limbajul Java, SQL, CLI
  2. Arhitectura JDBC
  3. Drivere si manageri de drivere
  4. Utilizare JDBC-Exemple
  5. Anatomia unei aplicatii JDBC
  6. Relatia dintre tipuri SQL si tipuri Java
  7. O aplicatie JDBC pe trei nivele
  8. Instructiuni preparate
  9. Tratarea campurilor speciale
  10. Exemple

1. Limbajul Java, SQL si CLI

JDBC este bazat pe doua standarde: SQL (Structured Query Language) si CLI (X/Open Call Level Interface). CLI este implementat in interfata Microsoft ODBC. ODBC a fost implementat in limbajul C, deci nu se putea  utiliza direct din Java deoarece s-ar fi stricat portabilitatea acestor aplicatii. O alta problema cu ODBC era ca fiind scris intr-un limbaj procedural in care caramida de constructie este functia era mai greu atasarea la Java, care este un limbaj obiectual. Multe functii C din ODBC  returneaza si pointeri void care violeaza caracteristica fundamentala a limbajului Java, siguranta tipului.

2. Arhitectura JDBC

Interfetele si clasele pentru JDBC se gasesc in pachetul java.sql. Clasele si interfetele utilizate intr-o aplicatie ce utilizeaza JDBC se gasesc pe figura urmatoare:

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:

  1. Se apeleaza la DriverManager cerand un driver specific pentru baza de date
  2. Driverul specific creaza legatura cu baza de date si returneaza un obiect de tip Connection
  3. Cu ajutorul obiectului de tip Connection se creaza un obiect Statement care contine si o cerere SQL catre baza de date
  4. Obiectul Statement returneaza rezultatele intr-un obiect ResultSet

3. Drivere si manageri de drivere

4. Utilizare JDBC-Exemple

Exemplul urmator ilustreaza structura tipica a unei aplicatii JDBC.
In primul exemplu vom lucru cu o baza de date  Access. Numele bazei de date va fi Inventory, la fel si numele singurei tabele continuta in baza de date.

Structura tabelei Inventory:
 

Nume_camp Tip_camp Lungime_camp
NAME Text 40
QUANTITY Numeric-Real 20

 Sursa Java:

import java.sql.*;

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();
                }
       }
}

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.

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();
                }
       }
}

5. Anatomia unei aplicatii JDBC

 

a. Instalarea unui driver

Primul lucru pe care trebuie sa faca o aplicatie care lucreaza cu baze de date este instalarea unui driver specific bazei de date. Exemple:
Class.forName("com.sybase.jdbc.SybDriver");
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
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: Internetul a rezolvat deja aceasta problema prin introducerea URL-urilor (Uniform resource Locator) care este format din: protocol//nume_host:port//cale. Pentru JDBC s-a adaptat aceasta specificatie. Un URL pentru o baza de date ar avea forma: jdbc:<protocol_secundar>:<nume_secundar>//nume_host:port//nume_baza_de_date.
 

b.Crearea conexiunii

La crearea conexiunii pe langa numele bazei de date se vor transmite si numele utilizatorului precum si parola acestuia. Daca sunt incarcate mai multe drivere pentru lucrul cu diferite baze de date, atunci se pune problema cum se alege driverul pentru conexiunea curenta. Clasa DriverManager este responsabila pentru acesta intreband fiecare driver daca poate realiza legatura cu baza de date specificata prin url. De exemplu un driver Oracle ar observa imediat ca in exemplul prezentat se lucreaza cu un alt tip de baza de date si ar refuza cererea. La realizarea conexiunii se creaza un obiect de tip Connection. De fapt Connection este o interfata, care asigura transmiterea datelor spre baza de date si obtinerea datelor din baza de date. Interfata furnizeza metode pentru a obtine informatii despre baza de date, inchid conexiunea cu baza de date si asculta mesajele sosite de la baza de date.
 

c. Accesul la baza de date

Interfata Connection contine si metoda createStatement() care ne va furniza un obiect Statement printr-un apel de urmatoarea forma:
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");

d. Prelucrarea rezultatelor

ResultSet este tot o interfata. Obiectul ResultSet contine rezultatul interogarii bazei de date, insa pointerul atasat acestei tabele puncteaza inaintea primului rand din tabela. Acest lucru ne este comod fiindca cu un singur ciclu while putem parcurge rezultatele obtinute:
while( rs.next()){
 System.out.println( rs.getString(1)+":"+rs.getFloat(2));
}
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:
ResultSet rs = stmt.executeQuery("A Query...");
int i = rs.getInt(1);
Daca rezultatul interogarii este vid atunci constructia de mai sus va genera exceptie SQLException.
ResultSet contine metode pentru accesul datelor din rezultat. Astfel metodele getXXX(int) si getXXX(String) returneaza valoarea dintr-o coloana specificata prin parametru si linia curenta. In locul XXX se pune un tip predfinit Java (tip primitiv-ex. int sau clasa de baza-ex. String).
 

6. Relatia dintre tipuri SQL si tipuri Java

 
Tip SQL
Tip JAVA
Metoda
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()

 

7.  O aplicatie JDBC pe trei nivele

In exemplele precedente aplicatiile noastre contineau si codul SQL. Vom rescrie primul exemplu cu baza de date inventory astfel incat vom crea clase speciale pentru toate operatiile posibile cu acesta tabela. Aplicatiile care au nevoie de operatii cu aceasta  tabela vor utiliza clasele speciale, astfel aceste clase realizand un nivel intermediar intre baza de date si aplicatia propriu zisa care lucreaza cu datele din tabela. Aceste clase incapsuleaza logica aplicatiei.

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 {
  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;
  }
}
Fisierul InventoryManager.java:
import java.sql.*;
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 +"'");
  }

}

Fisierul InventoryMain.java:
 
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();
    }
  }
}

8. Instructiuni preparate


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();
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;
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.

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.

9. Conversie SQL-Java

 
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()

10. Tratarea campurilor speciale

Multe SGBD-uri faciliteaza folosirea campurilor de obicete speciale ( binary large object - BLOB ). Aceste campuri sunt utilizate pentru stocarea datelor de lungime variabila care pot fi documente, foi de calcul, imagini, pagini Web etc. JDBC faciliteaza utilizarea acestor campuri de date. Exemplul urmator va scrie un asemenea camp de date intr-o baza de date, iar dupa aceea va citi un alt camp de date salvand rezultatul intr-un fisier. Exemplul nostru va stoca imagini de tip .gif. Ca prim pas vom crea cu Microsoft Access o baza de date cu urnatoarea structura:
Imagini.mdb cu tabela imagini:
 
Nume_Camp
Tip_Camp
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;
        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");
          }
}

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.
 
Noutati aduse de JDBC 2.0:
           Pentru a realiza operatile de ti batch se va utiliza metoda clasei Statement addBatch. Exemplu de utilizare:

//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();

11. Exemple:


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 );
        }
}