JDBC (Java DataBase Connectivity) 

JDBC (Java DataBase Connectivity)
7 Novembre 2007 Database
   





Introduzione

JDBC (Java DataBase Connectivity) è una API che consente ad un qualsiasi programma scritto in java di accedere ad un qualsiasi DBMS (JDBC è simile a ODBC, JDBC è specifico per i programmi scritti in java mentre ODBC si può usare indipendentemente dal linguaggio utilizzato). Le API JDBC si trovano all'interno del package java.sql, all'interno di questo package sono definite classi ed interfacce che consentono di effettuare diverse operazioni sul database
  • connessione al database;
  • esecuzione di istruzioni SQL: INSERT, UPDATE, DELETE, SELECT, eccetera;
Le classi e le interfacce più importanti sono
  • La classe DriverManager;
  • L'interfaccia Connection;
  • L'interfaccia Statement;
  • La classe ResultSet;
JDBC è costituito da due componenti
  • L'implementazione del driver, che dovrà essere conforme alle specifiche delle API java.sql, realizzata dal produttore del DBMS. Questa implementazione consisterà nell'implementazione dei vari metodi delle interfacce e delle classi costituenti il package java.sql.
  • L'implementazione dell'applicazione da parte del programmatore.
 

JDBC Driver

Esistono driver per molti database, i driver vengono classificati in 4 categorie
  • JDBC-ODBC bridge. Il driver utilizza ODBC, ogni chiamata a JDBC viene tradotta in una chiamata a ODBC.
  • driver API nativo. Converte le chiamate a JDBC in chiamate alle API del database.
  • protocollo di rete.
  • protocollo nativo.

Connessione al DBMS

Un esempio di codice per la connessione con un DBMS è il seguente

  1. try {
  2. Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");//si carica il driver
  3. }
  4. catch (ClassNotFoundException exc)
  5. {
  6. System.out.println("Errore: Driver jdbc non presente "+exc.getMessage());
  7. }
  8. try {
  9. String url="jdbc:odbc:basedati";//si specifica l'url del database
  10. String user = "utente";
  11. String passwd = "password";
  12. Connection con = DriverManager.getConnection(url, user, passwd);
  13. ...
  14. }
  15. catch (SQLException exc1)
  16. {
  17. System.out.println("Errore nella Connessione" +exc1.getMessage());
  18. }


Innanzitutto si carica il driver Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"), quindi si effettua la connessione specificando i dati dell'utente (nome e password) e l'url. In questo esempio si è utilizzato il driver sun.jdbc.odbc.JdbcOdbcDriver, in generale si può usare un qualsiasi altro driver a seconda del database a cui ci si deve connettere.
L'url ha la forma seguente jdbc:<subprotocol>:<subname>, dove
  • <subprotocol> è il nome del driver o del meccanismo che si usa per connettersi al database. Nell'esempio proposto si usa un bridge JDBC:ODBC

    1. String url="jdbc:odbc:basedati";

  • <subname> identifica la sorgente dei dati.
Nel caso in cui la sorgente dei dati si trovi su un host diverso il subname ha la seguente forma //hostname:port/subsubname

Lettura Dati dal DB

Si supponga di avere una tabella del tipo

  1. Tabella(
  2. Cognome Char(20),
  3. Nome Char(20),
  4. Provincia Char(20),
  5. Nazione Char(20),
  6. Luogodinasc Char(20),
  7. Datadinasc Char(20),
  8. primary key(....)
  9. )

per poter estrarre dati da questa tabella (accesso in lettura) si procede nel seguente modo

  1. try {
  2. Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
  3. }
  4. catch (ClassNotFoundException exc)
  5. {
  6. System.out.println("Errore: Driver jdbc non presente "+exc.getMessage());
  7. }
  8. try {
  9. String url="jdbc:odbc:dati";
  10. String user = "utente";
  11. String passwd = "password";
  12. Connection con = DriverManager.getConnection(url, user, passwd);
  13. Statement Tabella = con.createStatement();
  14. ResultSet r = Tabella.executeQuery("SELECT * FROM Tabella WHERE "+
  15. "(Cognome ='cognome+') and (Nome = '"+nome+"')");// si esegue la query
  16. if (r.next())
  17. {// se la query produce almeno una riga allora ....
  18. Data = r.getString("Datadinasc");
  19. luogo = r.getString("Luogodinasc");
  20. nazione = r.getString("Nazione");
  21. citta = r.getString("Provincia");
  22. }
  23. }
  24. catch (SQLException exc1)
  25. {
  26. System.out.println("Errore nell'Inserimento dei Dati" +
  27. exc1.getMessage());
  28. }

I passi da seguire sono quindi i seguenti
  • Si carica il driver

    1. try {
    2. Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
    3. }
    4. catch (ClassNotFoundException exc)

  • Si effettua la connessione con il database

    1. String url="jdbc:odbc:dati";
    2. String user = "utente";
    3. String passwd = "password";
    4. Connection con = DriverManager.getConnection(url, user, passwd);

  • Si crea un oggetto Statement

    1. Statement Tabella = con.createStatement();

  • Si crea un oggetto ResultSet, in cui saranno posti i risultati della query
  • Si effettua la query tramite l'istruzione Tabella.executeQuery("...."). In questo modo si specifica che la query si svolge nell'ambito della statement Tabella

    1. ResultSet r = Tabella.executeQuery("SELECT * FROM Tabella WHERE "+
    2. "(Cognome ='cognome+') and (Nome = '"+nome+"')");// si esegue la query

  • Si verifica se la query ha prodotto dei risultati (righe della tabella che soddisfano le condizioni specificate nella query). Se ha prodotto dei risultati si effettuano le operazioni sui dati. La verifica viene effettuata tramite r.next() (r.next() restituisce il valore booleano true se nel ResultSet r esiste una riga dopo la riga corrente)

    1. if (r.next())
    2. {// se la query produce almeno una riga allora ....
    3. Data = r.getString("Datadinasc");
    4. luogo = r.getString("Luogodinasc");
    5. nazione = r.getString("Nazione");
    6. citta = r.getString("Provincia");
    7. }

Inserimento Dati nel DB

Si supponga di avere una tabella del tipo

  1. Tabella(
  2. Cognome Char(20),
  3. Nome Char(20),
  4. Provincia Char(20),
  5. Nazione Char(20),
  6. Luogodinasc Char(20),
  7. Datadinasc Char(20),
  8. primary key(....)
  9. )

per poter inserire dati in questa tabella (accedere in scrittura) si procede nel seguente modo

  1. try {
  2. String url="jdbc:odbc:dati";
  3. String user = "utente";
  4. String passwd = "password";
  5.  
  6. /* si crea la Connessione con il database */
  7. Connection con = DriverManager.getConnection(url, user, passwd);
  8. con.setAutoCommit(false);
  9.  
  10. /* con questa istruzione si fa in modo che il commit avvenga solo se esplicitamente invocato dall'utente*/
  11. con.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
  12.  
  13. Statement dati = con.createStatement();// si crea un oggetto Statement
  14. String insert = "INSERT INTO Tabella VALUES ('"+cognome+
  15. "', '"+nome+"', '"+provincia+ "', '"+ nazione + "', '" +luogodinasc+
  16. "', '"+datadinasc+"'))";// si forma l'operazione di insert
  17. Tabella.executeUpdate(insert);// si esegue l'operazione di insert
  18. con.commit();// si effettua il commit
  19. Tabella.close();// si chiude lo Statement Tabella
  20. con.setAutoCommit(true);// si ripristina l'autocommit
  21. con.close();//si chiude la connessione
  22. }
  23. catch (SQLException exc1)
  24. {
  25. System.out.println("Errore nell'inserimento dei dati "
  26. +exc1.getMessage());
  27. }

I passi da seguire sono quindi i seguenti
  • Si carica il driver
  • Si effettua la connessione con il database
  • Si disabilita l'autocommit. In questo modo il commit sarà fatto solo su indicazione dell'utente e non automaticamente dal DBMS

    1. /* si crea la Connessione con il database */
    2. Connection con = DriverManager.getConnection(url, user, passwd);
    3. con.setAutoCommit(false);

  • Si imposta il grado di isolamento delle transazioni

    1. con.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);

    in questo esempio si imposta il livello che garantisce la massima integrità dei dati. Le transazioni sono serializzate, vengono eseguite una di seguito all'altra.
  • Si crea un oggetto Statement

    1. Statement dati = con.createStatement();// si crea un oggetto Statement

  • Si esegue l'operazione di INSERT tramite l'istruzione Tabella.executeUpdate(insert). In questo modo si specifica che l'inserimento si svolge nell'ambito dello statement Tabella

    1. String insert = "INSERT INTO Tabella VALUES ('"+cognome+
    2. "', '"+nome+"', '"+provincia+ "', '"+ nazione + "', '" +luogodinasc+
    3. "', '"+datadinasc+"'))";// si forma l'operazione di insert
    4. Tabella.executeUpdate(insert);// si esegue l'operazione di insert

  • Si effettua il commit

    1. con.commit();// si effettua il commit

  • Si chiude lo Statement

    1. Tabella.close();// si chiude lo Statement Tabella

  • Si ripristina l'autocommit

    1. con.setAutoCommit(true);// si ripristina l'autocommit

  • Si chiude la connessione

    1. con.close();//si chiude la connessione

I Trigger

I trigger sono delle soubroutine che è possibile invocare quando si verificano particolari condizioni sulla base di dati. Ad esempio si potrebbe realizzare una soubroutine che controlli tutte le righe che si vogliono inserire in una tabella e consenta di inserire una riga nella tabella solo se questa soddisfa determinate condizioni.
L'uso dei trigger varia da DBMS a DBMS, qui si vedrà l'uso con Oracle. Per creare un trigger si procede nel seguente modo
  • si crea una classe Java;
  • si attacca la classe Java ad una tabella;
  • si crea il Trigger;
Ad esempio sia check.class la classe che si vuole utilizzare, per attaccare alla tabella Tabella la classe si procede nel seguente modo

  1. ALTER TABLE Tabella ATTACH JAVA CLASS "check" IN 'c:\tmp\classes';
  2. .
  3. /

In questo modo si specifica che alla tabella Tabella deve essere attaccata la classe check che si trova in c:\tmp\classes. Per creare il relativo Trigger si procede nel seguente modo

  1. CREATE TRIGGER trigger
  2. AFTER INSERT ON Tabella
  3. FOR EACH ROW "check1" (new.campo1, new.campo2);
  4. .
  5. /

In cui si specifica che il trigger è costituito dalla soubroutine check1 presente nella classe check, deve essere invocato dopo l'inserimento di una riga nella tabella. E i parametri che riceve la soubroutine devono essere i campi campo1 e campo2 della tabella. La classe check è la seguente

check.java
  1. import java.sql.*;
  2. class check
  3. {
  4. public void check1 (Connection con, String campo1, String campo2)
  5. throws SQLException
  6. {
  7. String inizio, fine;
  8. boolean violazione = false;
  9.  
  10. Statement s = con.createStatement();
  11. ResultSet r = s.executeQuery("SELECT * FROM Tabella WHERE (Cognome = '"+
  12. campo1 +"') and (Nome = '"+ campo2+ "')");
  13.  
  14. while (r.next())
  15. {
  16. inizio = r.getString("Inizio");
  17. fine = r.getString("Fine");
  18. if ((parsedata.After(data1,inizio)&&parsedata.Before(data1,fine)) ||
  19. (parsedata.After(data2,inizio)&&parsedata.Before(data2,fine)))
  20. violazione = true;
  21. }
  22.  
  23. s.close();
  24. if (violazione)
  25. {
  26. System.out.println("Vincolo Violato");
  27. throw new SQLException("");
  28. }
  29. }
  30. .............// eventuali altri metodi
  31. }

Il formato del comando ALTER TABLE è il seguente

  1. ALTER TABLE <schema>.<tabella>
  2. ATTACH JAVA {SOURCE | CLASS} "classe"
  3. WITH CONSTRUCTOR ARGS (lista_parametri)

una volta attaccata la classe Java per staccarla si utilizza il comando

  1. ALTER TABLE <schema>.<tabella>
  2. DETACH [AND DELETE] JAVA CLASS "classe"

Da notare che per eliminare la classe si deve utilizzare il comando

  1. ALTER TABLE <schema>.<tabella>
  2. DETACH AND DELETE JAVA CLASS "classe"

se non si usasse il termine AND DELETE la classe sarebbe staccata dalla tabella, ma non sarebbe cancellata. Il formato del comando CREATE TRIGGER è il seguente

  1. CREATE [OR REPLACE] TRIGGER trigger_name
  2. {BEFORE | AFTER} [{INSERT | DELETE | UPDATE [OF (listacampi)]}]
  3. ON Tabella FOR EACH ROW soubroutine_name(listadiargomenti)

Il trigger può essere attivato prima o dopo {BEFORE| AFTER} un'operazione; l'operazione può essere INSERT, DELETE o UPDATE. Da notare che si possono usare solo le combinazioni BEFORE - {......} e AFTER - {........}