Come connettersi ai più comuni database con JAVA

JDBC - Java ed i Database

Internet postato da javaserver || 13 anni fa

Articolo offerto da javaserver.it

Una delle principali preoccupazioni nello sviluppo della maggior parte dei software è la necessità di curare l’interfacciamento della propria applicazione con una base dati.
In realtà JAVA ci offre numerose possibilità per accedere ai database: potremmo gestire tutto il lavoro internamente (scrivendo il codice necessario), oppure possiamo utilizzare le classi specifiche per l'accesso a quest’ultimi. Queste "classi" costituiscono JDBC: lo standard di accesso ai database per Java.
Esistono svariati tipi di database che possono essere utilizzati, ognuno ha i suoi punti di forza e di debolezza, Java consente di usarli praticamente tutti mediante l’utilizzo di una serie di classi che fungono da interfaccia astraendo le reali caratteristiche di ognuno di essi.

Tralasciano l’esistenza di database basati su una logica ormai deprecata ( ad esempio “Flat File” ), o su logiche relativamente meno utilizzate, poniamo l’attenzione sui cosidetti RDBMS.
Il termine Relational database management system (RDBMS) (sistema relazionale per la gestione di basi di dati) indica un sistema software progettato per consentire la creazione e manipolazione efficiente di database (ovvero di collezioni di dati strutturati) solitamente da parte di più utenti.


A seguire la lista di alcuni tra i più famosi RDBMS:
• Cloudscape
• Firebird SQL
• HSQLDB
• Ingres
• MaxDB
• MySQL
• PostgreSQL
• SmallSQL
• SQLite


JAVA per la gestione ottimale di quest’ultimi utilizza JDBC.

JDBC, e' un "livello di astrazione" definito da SUN che fornisce ad una applicazione (java ovviamente) un'insieme di classi per l'accesso e l'utilizzo di database.
In pratica, JDBC ci consente di utilizzare un database semplicemente invocando la creazione di una classe driver, che ci fornira' una serie di classi standard. Tramite i metodi di queste classi (metodi che sono specificati dalle interfacce indicate nello standard JDBC), siamo in grado di accedere ai nostri dati, leggerli e modificarli. Il tutto senza preoccuparci piu' di tanto del tipo di database o dello specifico dialetto SQL utilizzato. Il nostro driver si occupera' di tradurre il tutto e fornirci i risultati.
L'architettura di JDBC, così come quella di ODBC, prevede l’utilizzo di un “driver manager”, che espone alle applicazioni un insieme di interfacce standard e si occupa di caricare a “run-time” i driver opportuni per “pilotare” gli specifici DBMS. Le applicazioni Java utilizzano le "JDBC API" per parlare con il JDBC driver manager, mentre il driver manager usa le JDBC driver API per parlare con i singoli driver che pilotano i DBMS specifici. Esiste un driver particolare, il "JDBC-ODBC Bridge", che consente di interfacciarsi con qualsiasi driver ODBC in ambiente Windows.
Per completezza ricordo che ODBC è un'interfaccia nativa alla quale si può accedere tramite numerosi linguaggi. Nel caso di Microsoft Windows, questa libreria è una DLL.
In aggiunta al software ODBC, c'è bisogno di un driver specifico per poter accedere ad ogni diverso tipo di DBMS. ODBC permette ai programmi che lo usano di inviare ai database stringhe SQL senza che ci sia bisogno di conoscerne le API proprietarie. Genera automaticamente richieste che il sistema di database utilizzato sia in grado di capire ( in tal modo, i programmi possono connettersi a diversi tipi di database utilizzando più o meno lo stesso codice ).

Brevemente, a seconda del funzionamento, i driver JDBC si dividono in vari tipi:
Driver di tipo 1 (type 1 driver)
Questo tipo di driver traduce i metodi di chiamata JDBC in chiamate dirette verso un'altro driver fornito da un'altro produttore, cio' significa che entrambi i driver devono essere presenti perche' l'applicazione possa funzionare.

Si noti che la presenza di livelli multipli tra i due driver puo' peggiorare le prestazioni del sistema. Un tipico esempio di driver di questo tipo e' il bridge Jdbc/Odbc.
Driver di tipo 2 (type 2 driver)
Questo tipo di driver traduce le chiamate JDBC in chiamate alle API native del database stesso. Le prestazioni sono solitamente migliori di un tipo 1. Possono esserci pero' dei problemi di distribuzione in quanto il driver puo' richiedere la presenza di librerie del database stesso (che non sempre possono essere distribuite senza un'apposita licenza) oppure l'installazione sul client di software specifico del database (client software) che potrebbe essere piuttosto costoso.
Driver di tipo 3 (type 3 driver)
Questo tipo di driver e' il piu' comune da trovare, il driver traduce le chiamate JDBC in chiamate ad un database-server usando le API di comunicazione sulla rete (socket). L'unico problema pratico e' che e' richiesta la presenza di un database server o di una applicazione che ne emuli il funzionamento.

La configurazione richiede la conoscenza dell'indirizzo IP del server o del suo nome (se e' presente un DNS nella rete) e della porta che il server usa per ricevere chiamate.

Driver di tipo 4 (type 4 driver)
Questo tipo di driver traduce le chiamate JDBC in chiamate dirette al server usando le API della rete. Il driver e' (in sostanza) specifico per il singolo database. Questo tipo di driver presenta gli stessi problemi del tipo 3 sopramenzionato.

JDBC e' composto da varie classi che "cooperano" per fornire l'accesso ai dati del database.
In prima linea viene il Driver, che interpreta tutte le funzioni richiamate e le 'traduce' per il database, il Driver deve essere caricato in memoria, una volta fatto questo e' possibile utilizzarne le funzioni per creare una Connection, che incapsula il vero e proprio collegamento al database. Una Connection e' necessaria per poter fare qualunque altra cosa.
Una volta ottenuta la Connection siamo in grado di usare quest'ultima per produrre Statement, che verranno a loro volta usati per ottenere ResultSet, i quali contengono i dati.
Non e' sempre necessario l'uso di un ResultSet. Se vogliamo semplicemente eseguire delle modifiche sui dati (usando una UPDATE, INSERT o DELETE), possiamo utilizzare il metodo executeUpdate() direttamente sullo Statement.
Il driver viene caricato in memoria in due modi.

Usando un caricamento esplicito:

Class.forName("nome.della.classe.del.driver");

Oppure usando DriverManager per eseguire il caricamento:

DriverManager.registerDriver(new nome.della.classe.del.driver());

In entrambi i casi il nome della classe del driver e' necessario, tale nome e' (di solito) riportato nella documentazione che accompagna il driver stesso.
Una volta che il driver e' in memoria possiamo usare DriverManager per ottenere una connessione al database, per fare cio' ci serve un URL al database ( il percorso per accedere al database ).
Un esempio di URL potrebbe essere il seguente:
jdbc:mysql://localhost/

Una volta che abbiamo il nostro URL possiamo usare la DriverManager.getConnection() per ottenere una connessione al database. Se qualche cosa e' sbagliato (URL o altro), otterremo una eccezione.
A seguire un esempio di una classe che può essere di aiuto nell’instanziare/chiudere una connessione:
import java.sql.*;
import java.util.*;


public class Connetti{
           
    Connection con;
    Statement st;
    String dbSchema=null;
    String driver=null;
    String dsn=null;
   
    /** Connette il software al db utilizzando i paramentri presenti in resources/parametri.properties    */
    public void connect(){
        //---------------------------Parametri
        Properties properties = new Properties();
        try{
            properties.load(Connetti.class.getResourceAsStream("resources/parametri.properties"));
            driver=properties.getProperty("driver");
            dsn=properties.getProperty("dsn");
            dbSchema=properties.getProperty("dbSchema");
        }catch(Exception ex){
            System.err.println("File properties non trovato." + ex.getMessage());
            System.exit(1);
        }

        try{
            Class.forName(driver);
            con=DriverManager.getConnection(dsn);
        }catch(Exception e){
            System.out.println(e.getMessage());
            System.out.println("Driver: " + driver + "\nDsn=" + dsn);
        }
    }
   
    public void disconnect(){
        try{
            con.close();
        }catch(Exception e){
           System.out.println(e.getMessage());
        }
    }
   
    public Connection getConnection(){
        return con;
    }
}


Nel file resources/parametri.properties è opportuno inserire chiavi del tipo:


dbSchema=MYDBSCHEMA
driver=sun.jdbc.odbc.JdbcOdbcDriver // Driver Generico
dsn=Jdbc:odbc:NOMEDB //DNS di sistema / utente


In questo caso di è utilizzato un DNS precedentemente creato.

Per creare un driver ODBC su sistemi operativi Windows è necessario operare come segue:
1. dopo aver fatto doppio clic sull'icona "origine dati ODBC" dal pannello di controllo di windows, apparirà una finestra dalla quale bisogna scegliere il tipo di base di dati a cui ci si deve connettere.
2. Dopo aver cliccato su aggiungi, viene richiesto di scegliere il tipo di file del nostro database
3. Cliccando su fine, è possibile dare un nome all'origine dati e impostare il path del database da utilizzare.
Al termine di questa operazione il sistema operativo ha creato un file .dsn con il nome e tutti i riferimenti al nostro database, pronto per essere quindi utilizzato nelle nostre applicazioni.

Qualora non si voglia utilizzare un DNS di sistema/utente è possibile specificare come chiave dns una stringa simile alla seguente ( es:  mysql ):


jdbc:mysql://localhost/nomeDB?user=nomeUtente&password=pwdUtente


La classe Connetti potrà essere gestita molto agevolmente mediante una classe di esempio che la estende:


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

public class EseguiQuery extends Connetti
{    
   
    public int esegui(String query){
        try{
            //System.out.println(query);
            connect();
            st=con.createStatement();
            int num=st.executeUpdate(query);
           
            return num;
        }catch(Exception e){
            System.out.println(e);
            return 0;
        }finally {
            if (con != null){
                try {
                    st.close();
                    disconnect();
                   
                }catch (Exception E){
                   
                }
            }
        }
    }//end  esegui


Una volta ottenuta la connessione in ogni caso possiamo interrogare il database inviandogli una SELECT:
Per prima cosa costruiamo un PreparedStatement:

String sql="SELECT * FROM nomedellatabella WHERE nomecampo=?";
PreparedStatement p=conn.prepareStatement(sql);

Una volta ottenuto il PreparedStatement possiamo inserire il parametro di filtratura usando uno dei metodi setXxxxxx(), a seconda che il nostro parametro di filtro sia un numero o una stringa.

L'utilita' del PreparedStatement rispetto al costruire una stringa SQL "a mano" e' che gestisce lui le conversioni rispetto ai parametri passati in ingresso e dei vari formati numerici. Inoltre aggiunge i vari apici se necessario.

p.setString("questo e' il filtro");

Una volta impostati tutti i parametri, possiamo eseguire la query ed ottenre un ResultSet in risposta:

ResultSet r=p.execute();

Il ResultSet potrebbe essere vuoto o contenere 'n' record, per controllarne il contenuto si verifica se il metodo next() ritorna true o false:

while(r.next()) {
 // eseguo il controllo dei dati
}


Attenzione: per motivi di portabilita' tra i vari driver, i campi da un ResultSet possono essere letti solo nell'ordine in cui sono dichiarati nella SELECT ed una sola volta. Esistono driver che consentono di aggirare tali limitazioni, ma per evitare problemi e' sempre meglio seguire le specifiche.  SQL SERVER ad esempio risente di tale problematica.
L'accesso ai singoli campi del ResultSet puo' essere fatto sia usando il nome del campo, sia usando un numero progressivo:

rs.getXXXX(1); // ritorna il primo campo
rs.getXXXX("nomecampo"); // ritorna il campo "nomecampo"

Le funzioni getXxxxx() convertono automaticamente il tipo di campo nel tipo richiesto.

Una volta concluso l'utilizzo del ResultSet, possiamo chiuderlo per distruggere il corrispondente cursore e liberare risorse, ricordiamoci che il ResultSet e' connesso ad uno Statement, quindi chiudere lo Statement equivale a chiudere anche il cursore. Ma fare le cose pulite e' sempre meglio, quindi:

r.close(); // chiude il ResultSet
s.close(); // chiude lo Statement

 

Per completezza riporto un esempio web ( è possibile testarlo mediante un qualsiasi Application Server, Apache Tomcat in primis ).


L'esempio riportato in seguito aggiunge al database un record il cui campo è contenuto in una variabile dalla pagina JSP, che può quindi provenire da un form o da un'elaborazione dati:


<html><head>
<title>Esempio inserimento riga nel DataBase</title>
</head><body>
<%- deve essere importata il package java.sql.* per eseguire
le istruzioni SQL -%>
<%@ page language="java" import="java.sql.*" %>

<%
int valore = 25; //valore di esempio
int esito; //esito aggiornamento
Connection conn = null;

//carica il file di classe del driver per il ponte Odbc
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

//crea la connessione con l'origine dati
conn = DriverManager.getConnection("jdbc:odbc:miodb","","");

//crea lo statement
Statement st = conn.createStatement();

//esegue l'aggiornameto o l'inserimento
esito = st.executeUpdate("INSERT INTO tab1 (dati) values ("+valore+")"))
//se esito è uguale a 1 tutto è andato bene
if (esito == 1)
out.println("inserimento eseguito correttamente");
else
out.println("inserimento non eseguito");


rs.close();
conn.close();

%>
</body>
</html>

Articolo offerto da javaserver.it

http://www.javaserver.it