java & postgresql: da jdbc a pl/java - confsl.org€¦ · java & postgresql – luca...

182
Java & PostgreSQL: da JDBC a Pl/Java (passando per org.postgresql.Driver) Ing. Luca Ferrari, PhD [email protected] Italian PostgreSQL Users Group ITPug

Upload: lamduong

Post on 12-Sep-2018

274 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL: da JDBC a Pl/Java

(passando per org.postgresql.Driver)

Ing. Luca Ferrari, [email protected]

Italian PostgreSQL Users Group ITPug

Page 2: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 2 di 182

Synopsis

Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori Java che vogliano scrivere applicazioni capaci di gestire i dati contenuti in un cluster PostgreSQL.

In questa presentazione verranno mostrate le principali tecniche per la connettività Java verso PostgreSQL (tramite JDBC), il funzionamento interno del driver JDBC ufficiale nonché come “estendere” il database PostgreSQL con tecnologia Java tramite Pl/Java.

Gli esempi di applicazioni/database qui riportati sono a puro scopo didattico.

Page 3: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 3 di 182

Database didattico

CREATE TABLE corso(  corsopk serial NOT NULL, ­­ Chiave surrogata della tabella corso.  corsoid character varying(10), ­­ Chiave reale della tabella corso.   descrizione text,   ­­ Descrizione del corso   requisiti text, ­­ Elenco dei requisiti richiesti   data date, ­­ Data in cui si tiene il corso   n_ore integer, ­­ Numero di ore del corso.  materiale oid, ­­ Materiale didattico distribuito con il corso   ts timestamp with time zone DEFAULT ('now'::text)::timestamp,  CONSTRAINT corso_chiave_surrogata PRIMARY KEY (corsopk),  CONSTRAINT corso_chiave_reale UNIQUE (corsoid),  CONSTRAINT corso_n_ore_check CHECK (n_ore > 0 AND n_ore < 8))

Page 4: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 4 di 182

Database didattico

CREATE TABLE partecipante(  partecipantepk serial NOT NULL, ­­ Chiave surrogata della tabella   nome character varying(20), ­­ Nome del partecipante.  cognome character varying(20),   ts timestamp with time zone DEFAULT ('now'::text)::timestamp,  partecipanteid character varying(30),  CONSTRAINT partecipante_chiave_surrogata PRIMARY KEY (partecipantepk),  CONSTRAINT partecipante_chiave_reale UNIQUE (partecipanteid))

Page 5: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 5 di 182

Database didattico

CREATE TABLE j_corso_partecipante(  j_corso_partecipante_pk serial NOT NULL, ­­ Chiave surrogata   corsopk integer NOT NULL,  partecipantepk integer NOT NULL,

  CONSTRAINT j_corso_partecipante_chiave_surrogata PRIMARY KEY (j_corso_partecipante_pk),

  CONSTRAINT j_corso_partecipante_corsopk FOREIGN KEY (corsopk)      REFERENCES corso (corsopk) MATCH SIMPLE      ON UPDATE NO ACTION ON DELETE NO ACTION,

  CONSTRAINT j_corso_partecipante_partecipantepk FOREIGN KEY (partecipantepk)      REFERENCES partecipante (partecipantepk) MATCH SIMPLE      ON UPDATE NO ACTION ON DELETE NO ACTION)

Page 6: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 6 di 182

JDBC:applicazioni client

Page 7: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 7 di 182

JDBCJDBC (Java DataBase Connectivity) è un'API a livello SQL, ossia consente di inglobare comandi SQL nelle proprie applicazioni Java.

JDBC fornisce un'unica API CLI per l'accesso a database relazioni eterogenei. L'applicazione non necessita di adattamenti qualora il motore database cambi!

Applica il famoso concetto “Write Once, Run Everywhere” alla connettività database.

Si noti che l'idea non è nuovissima: ODBC si propone già come API unificata.

Non è indispensabile utilizzare JDBC!

Page 8: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 8 di 182

Application & Driver APIsJDBC può essere divisa in due parti fondamentali: Application API e Driver API.

Le Application API comprendono tutti gli elementi che un'applicazione Java usa per la connessione ad un database.

Le Driver API forniscono la base per la scrittura di un driver specifico per un determinato motore relazionale (es. PostgreSQL).

Grazie alla suddivisione in due parti, JDBC consente la scrittura di applicazioni portabili e database-independent.

All'URL http://developers.sun.com/product/jdbc/drivers

è possibile verificare l'esistenza di un driver JDBC per ogni motore relazionale conosciuto.

All'URL http://developers.sun.com/product/jdbc/drivers

è possibile verificare l'esistenza di un driver JDBC per ogni motore relazionale conosciuto.

Page 9: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 9 di 182

Application API vs Driver API

Java Application

DatabaseEngine

Application APIApplication API

Driver APIDriver API

Page 10: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 10 di 182

Livelli JDBCLe Driver API riconoscono quattro modalità di implementazione differenti, chiamate livelli o type dei driver JDBC:

● type 1 (JDBC-ODBC bridge): viene sfruttato un accesso ODBC (che deve esistere!).

● type 2 (Native API Drivers): il driver richiama, tramite JNI, codice nativo (es. C/C++) per la connettività.

● type 3 (Network Drivers): il driver si collega (via TCP/IP) ad un componente lato server che si interfaccia a sua volta con il database server.

● type 4 (Pure Java): il driver si collega direttamente al database server e comunica utilizzando un protocollo opportuno.

Page 11: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 11 di 182

Livelli JDBC: schema riassuntivo

Java Application

JDBC Type 1 ODBC Driver

DatabaseEngine

JDBC Type 2 Native Driver

JDBC Type 4

JDBC Type 3 Translator (e.g., local JDBC type 2)

Page 12: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 12 di 182

JDBC API

Page 13: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 13 di 182

Classi principali JDBCLe principali classi/interfaccie della libreria JDBC (java.sql.* e javax.sql.*) sono:

DriverManager: rappresenta un repository di istanze di Driver per ogni database server. Ci si rivolge a questa classe per creare una connessione ad uno specifico database (tramite un URL).

Driver: rappresenta il componente incaricato di stabilire la connessione con il database server. Può essere caricato all'interno del programma (es. tramite reflection) o specificando la proprietà jdbc.drivers nel momento di invocazione della JVM:

java ­Djdbc.drivers=org.postgresql.Driver myApp

Page 14: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 14 di 182

Classi principali JDBC (2) Connection: rappresenta una connessione al server

database. Tramite di essa è possibile interagire con il database stesso.

Statement: implementa una query SQL di qualunque tipo; viene usato per l'esecuzione di un comando sul database server. Si specializza in due sotto-interfaccie:

PreparedStatement: una query SQL precompilata e dotata di wrapping dei parametri.

CallableStatement: una query SQL che coinvolge una stored procedure.

Page 15: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 15 di 182

Classi principali JDBC (3) ResultSet: implementazione di un cursore SQL.

Contiene un riferimento alla riga dei risultati dall'esecuzione di un comando SQL. Disponde di una serie di metodi per il posizionamento rispetto alle righe e per l'estrazione dei valori di ogni colonna.

ResultSetMetaData: informazioni aggiuntive su un ResultSet, quali nomi e numero delle colonne, nome dello schema corrente, etc.

DatabaseMetaData: fornisce informazioni aggiuntive sul database stesso, quali versione, stored procedures, foreign keys, etc.

 SQLException: classe base di ogni eccezione JDBC.

Page 16: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 16 di 182

Tipica applicazione JDBC

Caricamento del driver necessario

Caricamento del driver necessario

Connessione al database

(Connection)

Connessione al database

(Connection)

Creazione di uno Statement

Creazione di uno Statement

Esecuzione dello Statement

Esecuzione dello Statement

Lettura del ResultSet

Lettura del ResultSet

[while ResultSet.next()]

Chiusra risorse associate a ResultSet e Statement

Chiusra risorse associate a ResultSet e Statement

Chiusura connessione

(Connection)

Chiusura connessione

(Connection)

Istanziazione Driver

Registrazione presso DriverMagaer

Simile all'esecuzione del comando

psql -h host -U utente db

Simile alla scrittura di una query sulla

linea di comando di psql<INVIO>

Page 17: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 17 di 182

Classi accessorie

La libreria fornisce una serie di classi aggiuntive per il mapping fra i tipi SQL e i tipi Java (Types), la gestione di Large Object Data (Clob,  Blob), date (Date), timestamp (Timestamp) e array (Array).

Come estensioni sono presenti anche le classi per la gestione dei data source (DataSource), dei row set (RowSet) e dei relativi eventi (e.g., RowSetEvent, StatetementEvent).

Page 18: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 18 di 182

Connessione al database

public static void main(String[] args) throws Exception{

try{

// classe del driver String driverName = "org.postgresql.Driver"; // url di connessione String databaseURL = "jdbc:postgresql://localhost/pgdaydb"; // caricamento della classe del driver

Class driverClass = Class.forName(driverName);

// creazione dell'istanza del driver Driver driver = (Driver) driverClass.newInstance();

Page 19: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 19 di 182

Connessione al database

// a questo punto il driver è registrato // presso il DriverManager (il driver di PostgreSQL // si registra automaticamente al DriverManager // appena la sua classe viene caricata)

// essendo il driver caricato e registrato posso // ottenere una connessione al database Connection connection = DriverManager.getConnection(databaseURL, "luca", // username "xxx" // password );

// una volta ottenuta una connessione al database // e' possibile interagire con esso per via di // oggetti di tipo Statement

Page 20: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 20 di 182

Creazione di uno Statement

// creazione di una query e di uno statement // da usare (la query mostra i corsi // che contengono java nella descrizione // e i relativi iscritti) String query = "SELECT c.corsoid, c.descrizione, c.data, c.n_ore, p.nome, p.cognome " + " FROM (corso c LEFT JOIN j_corso_partecipante j ON c.corsopk = j.corsopk) " + " LEFT JOIN partecipante p ON p.partecipantepk = j.partecipantepk " + " WHERE c.descrizione ilike '%java%' ";

// notare che lo Statement non e' ancora associato // a nessuna query

Statement statement = connection.createStatement();

// effettuo la query

ResultSet rs = statement.executeQuery(query);

Page 21: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 21 di 182

Analisi dei risultati: ResultSet // visualizzo i dati ottenuti dalla query: l'oggetto // ResultSet (rs) rappresenta un cursore dei risultati // che può essere scorso. while( rs.next() ){ System.out.println("######");

// posso ottenere i dati di una riga chiamando i // metodi getXXX (XXX = tipo di dato) e specificando // il numero della colonna (partendo da 1) o il suo // nome simbolico System.out.println("Id-Corso e descrizione: " + rs.getString(1) + " " + rs.getString(2)); System.out.println("Data e numero ore: " + rs.getDate(3) + " " + rs.getInt(4)); System.out.println("Iscritto: " + rs.getString("nome") + " " + rs.getString("cognome")); }

Page 22: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 22 di 182

Fine del programma e gestione delle eccezioni

// chiusura della connessione connection.close(); }catch(SQLException exception){ // eccezione SQL (problema nella query, errore del // server, etc.) }catch(ClassNotFoundException ex){ // classe del driver JDBC non trovata }catch(InstantiationException ex){ // errore di reflection (classe driver non // istanziabile) }catch(IllegalAccessException ex){ // errore di reflection (classe driver non // accessibile) } }}

Page 23: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 23 di 182

Esecuzione

Riassunto:● Caricamento del driver PostgreSQL (necessario solo all'avvio del thread principale dell'applicazione);● Creazione di una connessione specificando l'URL di connessione;● Creazione di uno Statement;● Esecuzione dello Statement;● Lettura dei risultati tramite il ResultSet;● Chiusura delle risorse.

Page 24: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 24 di 182

Come fa il DriverManager a scegliere il Driver opportuno?

Il DriverManager stabilisce quale sia il Driver da utilizzare per una connessione sulla base dell'URL di connessione (ogni driver accetta il proprio subprotocol):

protocol:subprotocol://HOSTNAME/DB_NAME

jdbc:postgres://localhost/pgdaydb

protocol: specifica il dominio di appartenenza di

questo URL. Indica che l'URL si riferisce ad una

connessione database (JDBC).

subprotocol: specifica il driver che si deve utilizzare. Viene utilizzato da DriverManager per fare il lookup

dell'istanza di Driver da restituire per gestire la connessione al database.

HOSTNAME: specifica l'indirizzo IP/nome dell'host

ove è in esecuzione il database.

DB_NAME: nome del database cui ci si intende

connettere.

Page 25: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 25 di 182

Considerazioni sul caricamento del driver

Il caricamento e l'istanziazione del driver JDBC può avvenire attraverso reflection o direttamente:// reflectionClass driverClass = Class.forName(“org.postgresql.Driver”);Driver driverInstance = driverClass.newInstance();

// istanziazione direttaDriver driverInstance = new org.postgresql.Driver();

I driver hanno sempre un costruttore di default senza argomenti,e quindi sono sempre istanziabili mediante reflection.

I driver hanno sempre un costruttore di default senza argomenti,e quindi sono sempre istanziabili mediante reflection.

Page 26: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 26 di 182

Considerazioni sul ResultSetUn ResultSet rappresenta un cursore sui risultati di una query. Il posizionamento fra le righe del cursore avviene tramite metodi specifici, quali next() e previous(). Ogni ResultSet è legato allo Statement che lo ha generato; se lo Statement viene alterato il ResultSet è automaticamente invalidato!

Esistono metodi per recuperare il valore di ogni colonna secondo il tipo di appartenenza (String, int, Date, ...):

<tipo_Java> get<tipo_Java>(int colonna)

<tipo_Java>get<tipo_Java>(String nomeColonna)

Ogni tipo SQL viene mappato in un tipo Java (e viceversa) mediante una specifica tabella di conversione (http://java.sun.com/j2se/1.3/docs/guide/jdbc/getstart/mapping.html). Tale tabella può essere estesa dall'utente per mappare i tipi user defined.

Si presti attenzione al fatto che, contrariamente alla logicadegli array, le colonne sono numerate partendo da 1!

Si presti attenzione al fatto che, contrariamente alla logicadegli array, le colonne sono numerate partendo da 1!

Page 27: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 27 di 182

ResultSet: errori frequenti

Si richiama getXXX(..) prima di aver chiamato almeno una volta

next().Si richiama getXXX(..) con un

indice di colonna errato (maggiore) rispetto al numero di colonne estratte

dalla query.

Page 28: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 28 di 182

Esecuzione di un INSERT/UPDATE/DELETE

// creazione di una query e di uno statement // da usare per inserire un nuovo corso String query = "INSERT INTO corso(corsoid,descrizione) VALUES('Java_adv', 'Corso avanzato su Java/JDBC') "; Statement statement = connection.createStatement(); // chiedo al server di eseguire la query e // di fornirmi il numero di righe "toccate" // tutte le query che modificano i dati vanno eseguite // attraverso il metodo executeUpdate(..) int rows = statement.executeUpdate(query); if( rows > 0) System.out.println("Inserimento effettuato con successo:" + rows + " righe inserite");

// chiusura della connessione connection.close();

Page 29: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 29 di 182

Esecuzione

Riassunto:● Caricamento del driver PostgreSQL (necessario solo all'avvio del thread principale dell'applicazione);● Creazione di una connessione specificando l'URL di connessione;● Creazione di uno Statement;● Esecuzione dello Statement;● Lettura del valore di ritorno dell'esecuzione dello Statement, tale valore indica il numero di righe toccate dalla query;● Chiusura delle risorse.

Page 30: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 30 di 182

Compilazione del driver

Page 31: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 31 di 182

Driver JDBC per PostgreSQL

PostgreSQL fornisce un driver JDBC conforme ai livelli 2,3 e 4. Il sito web del progetto è http://jdbc.postgresql.org, dal quale è possibile scaricare il driver (in formato binario o sorgente), navigare la documentazione Javadoc della parte public delle API.

E' presente una mailing list specifica: pgsql­[email protected]; gli attuali mantainer del Driver sono Dave Cramer, Kris Jurka, Oliver Jowett.

Page 32: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 32 di 182

Decompressione dei sorgenti

I sorgenti vengono rilasciati sotto forma di archivio compresso (tarball):

postgresql­jdbc­8.2­504.src.tgz

E' possibile decomprire i sorgenti usando il programma tar:

tar xzvf postgresql­jdbc­8.2­504.src.tgz

ottenendo una directory che contiene l'albero dei sorgenti

ls ­l postgresql­jdbc­8.2­50­rw­r­­r­­ 1 luca luca   259 2006­12­01 13:02 build.properties­rw­r­­r­­ 1 luca luca 21469 2006­11­29 05:00 build.xmldrwxr­xr­x 2 luca luca  4096 2006­12­01 13:22 doc­rw­r­­r­­ 1 luca luca  1542 2005­01­11 09:25 LICENSEdrwxr­xr­x 3 luca luca  4096 2006­12­01 13:22 org­rw­r­­r­­ 1 luca luca  3769 2004­10­23 00:24 README­rwxr­xr­x 1 luca luca   495 2004­11­07 23:15 update­translations.sh

Page 33: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 33 di 182

Compilazione: requisitiPer compilare il driver JDBC è necessario utilizzare Apache Ant (http://ant.apache.org), uno strumento di compilazione automatico per Java.

Ant è uno strumento concettualmente simile a make, ma ottimizzato per la gestione dei progetti Java. Esso stesso è scritto in Java, e questo ne facilita la fruibilità nello sviluppo di componenti Java.

Ant necessita della presenza di un file di build (solitamente build.xml) che contiene i task del processo di compilazione.

E' possibile specificare proprietà di compilazione mediante file di proprietà Java (file properties).

Page 34: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 34 di 182

Compilazione: build

Il file di build del driver è build.xml, e può essere omesso nell'invocazione di ant:

[luca@fluca:/sviluppo/java/contrib­src/postgresql­jdbc­8.2­504.src]$ antBuildfile: build.xml

all:prepare:check_versions:check_driver:driver:

compile:    [javac] Compiling 144 source files to /sviluppo/java/contrib­src/postgresql­jdbc­8.2­

504.src/build    [javac] Note: Some input files use or override a deprecated API.    [javac] Note: Recompile with ­Xlint:deprecation for details.    [javac] Note: Some input files use unchecked or unsafe operations.    [javac] Note: Recompile with ­Xlint:unchecked for details.

jar:      [jar] Building jar: /sviluppo/java/contrib­src/postgresql­jdbc­8.2­

504.src/jars/postgresql.jar

BUILD SUCCESSFULTotal time: 5 seconds[luca@fluca:/sviluppo/java/contrib­src/postgresql­jdbc­8.2­504.src]$

Page 35: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 35 di 182

Compilazione: risultato

Al termine del processo di compilazione viene generato un file postgresql.jar nella sottodirectory jars:

[luca@fluca:/sviluppo/java/contrib­src/postgresql­jdbc­8.2­504.src]$ ls ­l jars/total 436­rw­r­­r­­ 1 luca luca 441337 2007­03­31 11:22 postgresql.jar[luca@fluca:/sviluppo/java/contrib­src/postgresql­jdbc­8.2­504.src]$

Il file jar prodotto deve essere incluso nel classpath corrente affinché i programmi Java possano caricare il driver.

$ export CLASSPATH=`pwd`/jars/postgresql.jar:$CLASSPATH$ echo $CLASSPATH/sviluppo/java/contrib­src/postgresql­jdbc­8.2­504.src/jars/postgresql.jar:$

Page 36: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 36 di 182

Compilazione: risultato (2)

Un altro file importante viene generato: Driver.java

$ ls ­l org/postgresql/Driver.java*­rw­r­­r­­ 1 luca luca 29517 2007­03­31 11:21 org/postgresql/Driver.java­rw­r­­r­­ 1 luca luca 29523 2005­11­24 07:18 org/postgresql/Driver.java.in$

Prima della compilazione infatti il solo file Driver.java.in è presente. Il file Driver.java viene generato dinamicamente durante il processo di build, a seconda del livello JDBC del driver.

Page 37: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 37 di 182

Compilazione in Eclipse

(1) Definire un nuovo progetto basato su un file Ant esistente.

(2) Selezionare il file di build dalla directory dei sorgenti; specificare le proprietà del progetto.

Page 38: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 38 di 182

Compilazione in Eclipse (2)

(3) Ricercare il file build.xml tramite il modulo Ant di Eclipse.

Page 39: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 39 di 182

Compilazione in Eclipse (3)

(4) Eseguendo il file di build il progetto viene compilato.

Page 40: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 40 di 182

JDBC 2, 3 o 4?

Il driver JDBC di Postgresql supporta solo i livelli JDBC 2,3 e 4.

$ ls org/postgresql/ ­l...drwxr­xr­x 3 luca luca  4096 2006­12­01 13:23 jdbc2drwxr­xr­x 2 luca luca  4096 2006­12­01 13:23 jdbc3drwxr­xr­x 2 luca luca  4096 2006­12­01 13:23 jdbc4...$

L'albero dei sorgenti include i package per ogni versione JDBC.

La decisione su quale livello di driver (org.postgresql.Driver) viene presa durante il processo di build in base alla versione della JVM.In particolare la versione viene stabilita sulla base della Java Virtual Machine installata sul sistema.

Page 41: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 41 di 182

Driver.java.in

Il file org.postgresql.Driver.java.in è un file che viene tramutato nel driver Java vero e proprio durante il processo di build Ant.

Al suo interno il file Driver.java.in contiene dei segnaposti per le classi JDBC del livello opportuno.

Page 42: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 42 di 182

Scelta del livello JDBC

Il file build.xml contiene un target, check_versions, che controlla quale versione di JVM sia disponibile e, di conseguenza, quale versione di driver sia la più appropriata.

A seconda del livello stabilito viene impostata a true la proprietà jdbcX.

Page 43: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 43 di 182

Scrittura del livello JDBC

Il target driver si occupa della scrittura del file Driver sostituendo i marcaposto al suo interno con le classi del livello JDBC scelto.

Si noti che viene utilizzata la funzionalità di filtro (filter) di Ant per la sostituzione dei marcaposto (token) con le classi concrete.Diverse altre classi sono specificate con lo stesso procedimento.

Page 44: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 44 di 182

Scrittura del livello JDBC (2)

(1) Viene fatto un controllo sulla proprietà jdbcX, impostata precedentemente dal target check_versions.

Page 45: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 45 di 182

Scrittura del livello JDBC (3)

(2) Vengono impostati i token marcaposto da sostituire nel file Driver.java.in; ogni token specificato in build.xml trova corrispondenza con @token@ in Driver.java.in 

Page 46: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 46 di 182

Scrittura del livello JDBC (4)

(3) Il file Driver.java.in viene copiato in Driver.java applicando le condizioni di filtro.

Page 47: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 47 di 182

JDBC:tipi di Statement

Page 48: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 48 di 182

PreparedStatementUn PreparedStatement è un oggetto che memorizza uno statement SQL precompilato, che può essere parametrizzato e che può eseguire più efficientemente di uno Statement normale.

L'idea è di precompilare un comando SQL parziale e di completarlo successivamente, al momento in cui l'esecuzione sia necessaria.

Lo statement precompilato può poi essere riutilizzato più volte, cambiando eventualmente i parametri di esecuzione.

Essendo uno statement parametrizzabile, PreparedStatement fornisce dei metodi setXXX e getXXX per impostare i parametri di esecuzione.

Essendo uno statement parametrizzabile, PreparedStatement fornisce dei metodi setXXX e getXXX per impostare i parametri di esecuzione.

Un PreparedStatement è una sottointerfaccia di Statement!

Page 49: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 49 di 182

Prepared Statement // creazione di una query di inserimento e dello // statement precompilato per la sua esecuzioneString query = "INSERT INTO corso(corsoid,descrizione) VALUES(?,?) ";PreparedStatement statement = connection.prepareStatement(query);// inserisco alcuni corsi fornendo gli opportuni// parametri al serverint rows = 0;for(int i = 0; i < 10; i++){ String corsoid = "corso_" + i; String descrizione = "Nessuna descrizione "; // impostazione dei parametri nello statement statement.setString(1, corsoid); statement.setString(2, descrizione); // eseguo lo statement (non devo specificare una query // poiché lo statement è già stato precompilato) rows += statement.executeUpdate();}

Page 50: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 50 di 182

Considerazioni: PreparedStatement

Un PreparedStatement utilizza una sintassi speciale per le query SQL: ogni carattere ? viene usato come un marcatore per un parametro che verrà specificato in seguito.

In maniera duale ad un ResultSet vengono forniti dei metodi setXXX(numpar, value).

Il vantaggio dei PreparedStatement è che possono essere memorizzati e precompilati dal database server, aumentado le prestazioni nel caso di cicli (l'ottimizzatore potrebbe però stabilire piani diversi a seconda della selettività dei parametri).

Si presti attenzione al fatto chei parametri sono numerati partendo da 1!

Si presti attenzione al fatto chei parametri sono numerati partendo da 1!

Page 51: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 51 di 182

Statement vs PreparedStatement

Statement:✔ query statiche o poco variabili;

✔ nessuna necessità di escaping dei parametri.

PreparedStatement:✔ query parametriche;

✔ query base da eseguire ciclicamente a seconda di parametri run-time;

✔ escaping di parametri usati nella query stessa.

L'utilizzo di PreparedStatement è preferibile in molte situazioni!

Page 52: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 52 di 182

CallableStatementL'interfaccia CallableStatement viene usata in maniera analoga ai PreparedStatement per l'invocazione di una Stored Procedure.

La sintassi utilizzata per l'invocazione è la seguente:

{ call stored_procedure(?,?,...) }

Viene specificato il nome della stored procedure da invocare, seguito dalla lista di eventuali parametri.

La funzione viene invocata con il metodo executeQuery() che restituisce un ResultSet.

Page 53: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 53 di 182

JDBC:Utilizzo avanzato

Page 54: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 54 di 182

Esecuzione di comandi arbitrari

Si supponga di voler creare una vista definita come segue:

CREATE VIEW vw_partecipanti AS SELECT  c.corsoID,  c.descrizione,  c.data,  p.cognome, p.nome FROM partecipante p JOIN corso c ON c.corsoPK = p.corsoPK ORDER BY c.corsoID, p.cognome, p.nome

Come si può notare l'esecuzione della query non produce alcun risultato (in termine di numero di righe modificate) pur alterando la struttura del database.

Page 55: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 55 di 182

Esecuzione di comandi arbitrari

// creazione di uno statementStatement statement = connection.createStatement();// preparazione della queryString sql = "CREATE VIEW vw_partecipanti AS   SELECT c.corsoID, c.descrizione, c.data, p.cognome, p.nome   FROM partecipante p JOIN corso c ON c.corsoPK = p.corsoPK    ORDER BY c.corsoID, p.cognome, p.nome";

int result = statement.executeUpdate(sql);System.out.println("Risultato di esecuzione: " + result);

Page 56: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 56 di 182

JDBC: creazione della procedura

 // creazione di uno statement Statement statement = connection.createStatement(); // preparazione della funzione StringBuffer buffer = new StringBuffer(1000); buffer.append( "CREATE OR REPLACE FUNCTION ...”); 

 buffer.append("  RETURNS void AS $BODY$ DECLARE ...”);

 // chiamata della stored procedure int result = statement.executeUpdate(buffer.toString());System.out.println("Risultato della creazione della     

                                 procedura: " + result);

Page 57: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 57 di 182

JDBC: invocazione della procedura

// invocazione della funzioneString sql = "{ call f_nuovo_corso(?,?,?) }";          // preparazione della chiamata e impostazione dei parametriCallableStatement cStatement = connection.prepareCall(sql);cStatement.setInt(1, 2);cStatement.setString(2, "Corso2009");Calendar calendar = Calendar.getInstance();calendar.set(Calendar.DAY_OF_MONTH, 7);calendar.set(Calendar.MONTH, 7);calendar.set(Calendar.YEAR, 2009);cStatement.setDate(3,           new  java.sql.Date(calendar.getTimeInMillis()) );// invocazione della funzioneResultSet resultSet = cStatement.executeQuery();// in questo caso non ho risultati....            

Page 58: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 58 di 182

CallableStatementL'interfaccia CallableStatement viene usata in maniera analoga ai PreparedStatement per l'invocazione di una Stored Procedure.

La sintassi utilizzata per l'invocazione è la seguente:

{ call stored_procedure(?,?,...) }

{ ?= stored_procedure(?,?,...) }

Viene specificato il nome della stored procedure da invocare, seguito dalla lista di eventuali parametri.

L'invocazione avviene tramite il metodo executeQuery() che restituisce un ResultSet.

E' possibile registrare parametri di input/output e ottenerne il valore direttamente dallo statement.

Page 59: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 59 di 182

Batch

L'interfaccia Statement consente l'esecuzione batch di una serie di comandi SQL:

public void addBatch(String sql)

mediante tale metodo è possibile assegnare una serie di istruzioni SQL da eseguire sequenzialmente in un momento successivo.

L'esecuzione del gruppo di comandi avviene mediante il metodo executeBatch(), che restituisce i singoli valori di ritorno dell'esecuzione di ogni comando.

public int[] executeBatch()

Page 60: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 60 di 182

Batch: un primo esempio // creo uno statementStatement statement = connetion.createStatement();          // imposto i comandi da eseguire nel batchString sql = “INSERT INTO ...”;// aggiungo questa istruzione al batchstatement.addBatch(sql);// nuova istruzione da aggiungere al batchsql = “INSERT INTO ...”;statement.addBatch(sql);...// eseguo il batchint result[] = statement.executeBatch();// controllo i risultati di ogni comandofor(int i=0; i<result.length; i++)

System.out.println(“Comando “+(i+1)+” risultato “+ result[i]);

Page 61: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 61 di 182

Batch & PreparedStatement

L'utilizzo dei batch agevola i casi in cui siano presenti cicli.

E' possibile utilizzare oggetti PreparedStatement al fine di unire i vantaggi di un'esecuzione batch e della parametrizzazione dei comandi SQL.

// query parametriString sql = “UPDATE corso SET descrizione=? WHERE corsoID=?”;PreparedStatement statement = connection.prepareStatement(sql);for(int i=0; i< 10; i++){ statement.setString(1, descrizione[i]); statement.setString(2, id[i]); statement.addBatch();}

// esecuzioneint result[] = statement.executeBacth();

Page 62: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 62 di 182

Batch: considerazioni

Solitamente l'utilizzo di un batch risulta più efficiente che l'esecuzione delle singole operazioni in modo separato (ottimizzazione da parte del driver).

Si presti attenzione al fatto che un batch è differente da una transazione, tuttavia può essere usato per rendere più compatto (e quindi leggibile) il codice di una transazione.

Page 63: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 63 di 182

Scrollable ResultSetUno scrollable ResultSet è un tipo particolare di ResultSet che offre una funzionalità di scrolling.

E' possibile muoversi attraverso le righe in avanti e indietro, saltare righe, posizionarsi in modo relativo o assoluto, etc.

Il tipo di scrolling deve essere specificato a livello di creazione dello Statement, specificando il tipo di scrolling (e di aggiornabilità) come parametri del metodo createStatement(..), in modo da consentire al database di gestire le risorse.

Il driver PostgreSQL utilizza internamente un Vector percontenere le tuple del ResultSet; mediante un indiceriesce a garantire lo scrolling di tali tuple.

Il driver PostgreSQL utilizza internamente un Vector percontenere le tuple del ResultSet; mediante un indiceriesce a garantire lo scrolling di tali tuple.

Page 64: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 64 di 182

Tipo di scrollingIl tipo di un ResultSet può essere specificato tramite tre valori predefiniti:

ResultSet.TYPE_FORWARD_ONLY  (1003)  è il classico ResultSet che può essere consultato solo in avanti.

ResultSet.TYPE_SCROLL_INSENSITIVE  (1004) può essere consultato in entrambe le direzioni ma non riflette cambiamenti fatti al database mentre è aperto.

ResultSet.TYPE_SCROLL_SENSITIVE  (1005)  può essere consultato in entrambe le direzioni e riflette ogni cambiamento apportato.

Il tipo di scrolling può essere ricavato a tempo di esecuzione tramite il metodo

int ResultSet.getType();

Page 65: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 65 di 182

Posizionamento all'interno del ResultSet

L'interfaccia ResultSet mette a disposizione diversi metodi di posizionamento:

beforeFirst(), afertLast() restituiscono true se si ci si trova oltre la prima o l'ultima riga. next(),  previous() muovono di una riga in avanti o indietro il cursore.absolute(int), relative(int) spostano il cursore alla riga specificata in modo assoluto o relativo (dalla posizione corrente). Un indice negativo considera lo spostamento all'indietro (es. -1 sposta all'ultima riga nel caso di posizionamento assoluto o alla riga precedente nel caso di posizionamento relativo).getRow() ritorna il numero di riga (valore assoluto) a cui si è posizionati correntemente.

Page 66: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 66 di 182

Esempio di scrollable ResultSet // ottengo il result set di una queryResultSet rs = statement.executeQuery();

while( rs.next() ){ // esecuzione in avanti (solo righe pari) int numero_riga = rs.getRow();

if( (numero_riga % 2) == 0 ){ System.out.println("######");

System.out.println("Id-Corso e descrizione: " + rs.getString(1) + " " + rs.getString(2)); }}

// torno indietro e processo solo le righe dispariwhile( rs.previous() ){

int numero_riga = rs.getRow();

if( (numero_riga % 2) != 0 ){...}}

Page 67: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 67 di 182

ResultSet aggiornabili

JDBC v2 permette di aggiornare (inserire, modificare, cancellare) le righe di una query usando direttamente il ResultSet corrispondente.

In sostanza non si è obbligati a creare un comando di update/insert ma si può modificare il valore di una colonna direttamente operando sui risultati di una query.

E' un tentativo di nascondere i dettagli SQL e di rendere il sistema più Object-Oriented.

Page 68: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 68 di 182

ResultSet aggiornabili (2)

Per rendere tutto ciò possibile l'interfaccia ResultSet include una serie di metodi simili a quelli per la definizione dei parametri di un PreparedStatement. Ad esempio:

updateString(int column, String value);updateInt(int column, int value);

...Esistono poi dei metodi speciali per aggiornare/inserire/cancellare e aggiornare i valori della riga corrente:

updateRow();insertRow();deleteRow();refresh();

Page 69: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 69 di 182

Tipo di aggiornabilità

Il tipo di un ResultSet, relativamente alla sua aggiornabilità, può essere specificato tramite due valori predefiniti:

ResultSet.CONCUR_READ_ONLY  (1007)  è il classico ResultSet che non può essere aggiornato.

ResultSet.CONCUR_UPDATABLE  (1008) può essere aggiornato e quindi supporta inserimento, modifica e cancellazione di righe.

Il tipo di aggiornabilità può essere ricavato a tempo di esecuzione tramite il metodo

int ResultSet.getType();

Page 70: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 70 di 182

Inserimento di una riga

L'inserimento di una riga in un ResultSet è un'operazione leggermente più complessa rispetto all'aggiornamento.

Occorre infatti istruire il ResultSet affinché prepari posto per i valori della riga che si vuole inserire, assegnare tali valori e confermare (salvare) l'inserimento della nuova riga.

Se il ResultSet è di tipo scroll sensitive la riga inserita sarà visibile senza bisogno di aggiornamenti del ResultSet stesso.

Page 71: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 71 di 182

Inserimento di una riga: un approccio visuale

La procedura di inserimento di una nuova riga è simile alla procedura usata in molti programmi visuali di accesso e modifica dei database:

(1) viene ottenuto il ResultSet relativo ad una specifica query;

(2) si passa oltre l'ultima riga, usando una riga vuota come spazio temporaneo per l'inserimento dei valori. Si usa il metodo moveToInsertRow();

(3) vengono salvati i valori inseriti. Si usa il metodo insertRow().

Page 72: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 72 di 182

Inserimento di una riga in un ResultSet

// processo il result set ed inserisco una nuova riga (max 4)// ad ogni riga pari (non significa DOPO ogni riga pari!)while (rs != null && rs.next()) {    int numero_riga = rs.getRow();            if( ((numero_riga % 2) == 0) && inserite < 4 ){     // inserisco una nuova riga in fondo al result set     rs.moveToInsertRow();     rs.updateString(1, "CorsoRS" + numero_riga);     rs.updateString(2, "Prova di inserimento da ResultSet");     rs.insertRow();     // torno alla riga cui ero prima dell'inserimento     rs.moveToCurrentRow();     inserite++;   }   String corsoID = rs.getString(1);   String descrizione = rs.getString(2);   System.out.println("Riga numero " + numero_riga + " ­ "                               + corsoID + " = " + descrizione);}

Page 73: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 73 di 182

JDBC:Transazioni

Page 74: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 74 di 182

JDBC e transazioni

Il driver JDBC lavora in modalità auto-commit: ogni istruzione eseguita tramite uno Statement viene automaticamente confermata.

E' come se si eseguissero transazioni mono-comando.

Per prendere il controllo sulla transazione e segnarne l'inizio e la fine (BEGIN-END) è necessario disabilitare l'auto-commit del driver e forzare un esplicito commit/rollback.

Page 75: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 75 di 182

Schema di funzionamento

// disabilito auto commit connection.setAutoCommit(false); // effettuo del lavoro, INSERT, UPDATE, ecc. e tengo // traccia se devo fare un rollback ...

rollback = true;

// tutto fattoif( ! rollback ) connection.commit();else connection.rollback();

connection.setAutoCommit(true); // ripristino auto-commit

Page 76: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 76 di 182

Transazioni: livello di isolamento

E' possibile impostare il livello di isolamento di una transazione. I livelli di isolamento sono memorizzati come costanti (interi) nell'oggetto Connection e possono valere:

Connection.TRANSACTION_READ_COMMITTED impedisce che ci siano dirty-reads, ma consente unrepeatable e phantom reads;

Connection.TRANSACTION_READ_UNCOMMITTED non consente nessun tipo di dirty, unrepeatable e phantom reads;

Connection.TRANSACTION_REPEATABLE_READ consente solo phantom reads;

Connection.TRANSACTION_SERIALIZABLE le transazioni sono serializzabili.

Page 77: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 77 di 182

Transazioni: riassunto & considerazioni

L'inizio di una transazione deve essere eplicitato a livello di connessione (Connection) disabilitando la modalità di auto-commit.

Tutte le operazioni eseguite tramite uno Statement dopo aver disabilitato l'auto-commit fanno parte della transazione.

La fine di una transazione deve forzare un esplicito commit o rollback sulla connessione.

Una volta terminata la transazione è necessario riabilitare l'auto-commit, altrimenti le istruzioni succesive faranno parte di una nuova transazione (il driver non ri-abilita da solo l'auto-commit!).

Page 78: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 78 di 182

JDBC:DataSource

Page 79: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 79 di 182

DataSourceLe API JDBC 2 introducono nel package javax.sql l'interfaccia DataSource che si comporta come un wrapper attorno alla connettività di un un database.

In sostanza un DataSource è un'oggetto che contiene tutte le informazioni che servono per la connettività verso la sorgente dati (database) quali URL, username, password, auto-commit mode, etc.

Lo scopo dei DataSource è quello di disaccoppiare l'impostazione dei parametri di connettività (tipicamente amministrativi) dal loro utilizzo (tipicamente applicativo).

Diverse applicazioni possono condividere la stessa DataSource (ad esempio tramite JNDI); una modifica nel DataSource (ad esempio modifica all'URL) non necessita nessun intervento sulle applicazioni che lo utilizzano.

Page 80: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 80 di 182

ConnectionPoolDataSourceUn tipo particolare di sorgente dati è il ConnectionPoolDataSource che consente di gestire le connessioni in pool.

L'idea è quella di mantenere le connessioni al database in una cache, in modo da renderle disponibili a diversi componenti man mano che vengono richieste. Non venendo aperte/chiuse di continuo, le connessioni vengono erogate con tempi di risposta più bassi rispetto all'approccio classico.

L'utilizzo delle sorgenti dati con pool richiede però qualche passaggio in più rispetto all'uso delle sorgenti dati semplici. Viene comunque garantito il disaccoppiamento fra la parte amministrativa e quella applicativa.

Page 81: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 81 di 182

DataSource & PostgreSQL

I driver JDBC di PostgreSQL mettono a disposizione due tipi principali di DataSource (contenute nel package org.postgresql.ds):

PGSimpleDataSource un DataSource senza alcun tipo di pooling. Implementa javax.sql.DataSource e dispone di un metodo getConnection() che fornisce la connessione al database. Ad esempio:

DataSource dataSource = new PGSimpleDataSource();...Connection connection = dataSource.getConnection();

Page 82: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 82 di 182

DataSource & PostgreSQL

PGPoolingDataSource un DataSource con capacità di pooling delle connessioni. Implementa javax.sql.ConnectionPoolDataSource e mette a disposizione un metodo getPooledConnection() che fornisce un oggetto javax.sql.PooledConnection, al quale si può richiedere la connessione tramite getConnection(). Ad esempio:

ConnectionPoolDataSource dataSource = 

                    new PGConnectionPoolDataSource();...

PooledConnection pooledConnection = 

                   dataSource.getPooledConnection();Connection connection = pooledConnection.getConnection();

Page 83: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 83 di 182

Impostazione delle proprietà di una sorgente datiLe proprietà delle sorgenti dati vengono impostati tramite metodi set (e lette dai relativi metodi get secondo le specifiche Java Beans).

Solitamente gli ambienti server mettono a disposizione dei file di configurazione (es. XML) che consentono di specificare le varie proprietà. L'ambiente container (es. Tomcat) si farà poi carico di leggere tale file di configurazione e di impostare le proprietà di connessione nella sorgente dati.

serverName Indirizzo IP o nome del server databasedatabaseName Nome del database cui collegarsiportNumber Porta a cui collegarsi tramite TCP/IPuser Utente da usare per la connessionepassword Password per l'utente di cui soprainitialConnections Numero di connessioni da creare all'avviomaxConnections Massimo numero di connessioni                      istanziabili

Page 84: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 84 di 182

Esempio di uso di DataSource// creazione di un data source per PostgreSQLPGConnectionPoolDataSource dataSource =            new org.postgresql.ds.PGConnectionPoolDataSource();

// impostazione dei parametri di connessionedataSource.setDatabaseName("pgdaydb");dataSource.setUser("luca");  dataSource.setPassword(null);dataSource.setServerName("localhost"); dataSource.setDefaultAutoCommit(true);

// ora il datasource andrebbe esportato e reso disponibile// ad altre applicazioni, ad esempio tramite JNDI...// prelevo la sorgenti datiConnectionPoolDataSource source =                          (ConnectionPoolDataSource) dataSource;// oppure// PooledConnection pooledConnection = //                          source.getPooledConnection();

Page 85: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 85 di 182

SpringLa libreria Spring fornisce un sottoinsieme di classi di utilità per la connessione a database e la realizzazione di DAO.<!­­  il template jdbc che deve essere inizializzato con una datasource  ­­><bean id="jdbcTemplate"       class="org.springframework.jdbc.core.JdbcTemplate">       <constructor­arg>            <ref bean="datasource" />        </constructor­arg></bean>

<!­­ inmpostazione della datasource ­­><bean id="datasource"class="org.springframework.jdbc.datasource.DriverManagerDataSource">   <property name="driverClassName" value="org.postgresql.Driver" />   <property name="url" value="jdbc:postgresql://192.168.1.2/hrpmdb" />   <property name="username" value="hrpm" />   <property name="password" value="HrPm" /></bean>

Page 86: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 86 di 182

Spring lato applicativo

Lato applicativo occorre ottenere il jdbctemplate e usare uno dei suoi metodi per effettuare le query.

// insert the address in the databasethis.jdbcTemplate.update( this.getInsertQuery(),                           toInsert.getPrimaryKey(),                           toInsert.getStreet(),                           toInsert.getCity(),                           toInsert.getCountry(),                           toInsert.getState(),                           toInsert.getPhone(),                           toInsert.getZipCode(),                           toInsert.getFax() );

Page 87: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 87 di 182

Java Transaction API

Page 88: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 88 di 182

Java Transaction API (JTA)

La Java Transaction API (JTA) è un insieme di interfaccie e classi che forniscono una astrazione verso un sistema di transazioni distribuite.

Una transazione distribuita è una transazione che coinvolge più risorse (database) allo stesso momento. La necessità di mantenere coerenti le risorse (database) richiede protocolli appositi per la decisione di commit/rollback globale.

La JTA si appoggia su una implementazione specifica del server, chiamata Java Transaction Service (JTS). Il JTS è responsabile di implementare il Transaction Manager, che è il componente che prende la decisione globale sul commit/rollback.

Page 89: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 89 di 182

Transazioni distribuite in Java

Java Application

DatabaseEngine

User Transaction Interface

User Transaction Interface

JDBC DriverJDBC Driver

DatabaseEngine

Transaction ManagerTransaction Manager

Application Server

Page 90: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 90 di 182

Transazioni distribuite in Java

L'interfaccia javax.transaction.UserTransaction

consente di definire i confini di una transazione distribuita, ossia quale sezione di codice debba essere eseguito come transazione. La sua implementazione dipende dal container che si sta utilizzando.

L'interfaccia javax.transaction.TransactionManager consente la gestione delle risorse di transazione al container.

L'interfaccia javax.transaction.xa.XAResource rappresenta una risorsa XA da gestire.

Page 91: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 91 di 182

Transazioni distribuite: Commit a due fasi

Il protocollo di commit a due fasi (Two Phase Commit) è un algoritmo distribuito che consente a tutti i nodi che partecipano alla transazione di accordarsi all'unisono per un commit o un rollback.

(fase 1: pre-commit) Il coordinatore invia un query-to-commit a tutti i partecipanti. Ogni partecipante effettua le operazioni, aggiorna i propri log (undo e redo) e invia un messaggio di commit o rollback al coordinatore.

(fase 2: commit) Quando il coordinatore ha tutti i messaggi dei partecipanti prende la decisione:

se tutti hanno inviato un commit il coordinatore conferma il commit ai partecipanti che rendono permanenti le modifiche e inviano un acknowledgement. Il coordinatore chiude la transazione quando riceve tutti gli acknoweldgement.

se almeno un partecipante ha inviato un rollback il coordinatore invia un rollback ai partecipanti, che annullano la loro transazione locale e confermano il rollback al coordinatore.

Page 92: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 92 di 182

Risorse X/Open CAE

X/Open CAE specifica gli oggetti e le interfaccie da usare nelle transazioni distribuite (Distributed Transaction Processing: the XA specification): definisce cosa un gestore di risorse debba fare per supportare le transazioni distribuite.

Il supporto XA del driver è fondamentale per consentire l'uso con transazioni distribuite.

Il driver JDBC di PostgreSQL supporta XA dalla versione 8.1, anche se senza supporto per il transaction interleaving (capacità di usare la stessa connessione per differenti transazioni contemporanee).

Page 93: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 93 di 182

JTA & Driver JDBC

Nonostante il Transaction Manager sia il componente più importante, il driver JDBC deve fornire supporto a XADatasource, XAConnection e XAResource.

Le classi di più alto livello JTA, quali ad esempio UserTransaction e TransactionManager non sono nello scope di un driver JDBC quanto di un application server.

Page 94: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 94 di 182

Identificazione di una transazioneUna transazione è identificata da un Transaction ID composto da tre parti fondamentali:

format ID: specifica il naming schema delle transazioni (es. OSI CCR – Commitment, Concurrency e Recovery).

branch qualifier: un branch rappresenta una singola richiesta ad un resource manager.

global transaction id: un identificativo globale della transazione in tutto il sistema.L'implementazione di un transaction id (javax.transaction.xa.Xid) dipende dal container/sistema utilizzato.

Page 95: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 95 di 182

Creazione di uno Xidpublic class PgDayXid implements Xid{  // Il formatID specifica quale specifica si sta usando.   // Se vale 0 allora si sta usando la specifica OSI­CCR   // (OSI­Commitment, Concurrency and Recovery).  // Se vale ­1 allora l'Xid non è valido,   // mentre se è superiore a zero allora indica   // una specifica proprietaria.  protected int formatID = 0;      // identificativo globale della transazione corrente. (0­64 bytes)  protected byte[] globalTransactionId = null;      // identificativo del ramo della transazione corrente. (0­64 bytes)  protected byte[] branchQualifier = null;      public byte[] getBranchQualifier() { return this.branchQualifier; }

  public int getFormatId() { return this.formatID; }

  public byte[] getGlobalTransactionId() { return                                         this.globalTransactionId; }

Page 96: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 96 di 182

Creazione di uno Xid  public void setGlobalTransactionId(byte[] gtid){    // tengo conto solo dei primi 64 bytes...    if( gtid != null && gtid.length > 64 ){      System.arraycopy( gtid, 0, this.globalTransactionId, 0, 64 );    }    else       this.globalTransactionId = gtid;  }      public void setBranchQualifier(byte[] bq){      ...  }      public void setFormatId(int id){       ...  }}

Page 97: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 97 di 182

Utilizzo di JTApublic static void main(String[] args) throws Exception {

  // 1) creazione di un datasource agganciato al database  PGXADataSource dataSource = new PGXADataSource();  dataSource.setDatabaseName("pgday"); // nome del database  dataSource.setUser("luca");    // username  dataSource.setPassword("fluca"); // password dell'utente  dataSource.setServerName("localhost"); // indirizzo di connessione                                           // del database

  // 2) ottengo una connessione al database  System.out.println("Tentativo di connessione al database...");  XAConnection connection = dataSource.getXAConnection();  System.out.println("Connesso!");

  // 3) ottengo una risorsa per proteggere la transazione  XAResource resource = connection.getXAResource();

Page 98: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 98 di 182

Utilizzo di JTA  // 4) devo avere un id della transazione che sto per usare  PgDayXid identifier = new PgDayXid();  identifier.setBranchQualifier(       new byte[] {0x01, 0x02, 0x03, 0x04, 0x05});  identifier.setGlobalTransactionId(       new byte[] {0x05, 0x04, 0x03, 0x02, 0x01});  identifier.setFormatId(100);

  // 5) eseguo la transazione  try{    // 6) inizio della transazione    resource.start(identifier, XAResource.TMNOFLAGS);

    // 7) esecuzione di operazioni JDBC    Connection jdbcConnection = connection.getConnection();    jdbcConnection.setAutoCommit(false);  // bug del driver!    Statement statement = jdbcConnection.createStatement();    String sql = "INSERT INTO corso(corsoID, descrizione)                                     VALUES('XA1', 'Corso su JTA')";    int inserted = statement.executeUpdate(sql);    System.out.println("Sono state inserite " + inserted + " righe");

    

Page 99: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 99 di 182

Utilizzo di JTA    // 8) ho terminato la transazione    resource.end(identifier, XAResource.TMSUCCESS);        // 9) controllo se il transaction manager consente il commit    // (fase di precommit)    int commit = resource.prepare(identifier);     System.out.println("Valore del prepare " + commit);    if( commit == XAResource.XA_OK )        // commit definitivo        resource.commit(identifier,    // identificativo transazione                        false);        // false = commit 2 fasi    else        resource.rollback(identifier);      }catch(XAException e){    e.printStackTrace();  }}

Page 100: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 100 di 182

Sospensione di una transazione

E' possibile sospendere una transazione distribuita per dare modo al processo corrente di eseguire operazioni locali.

E' necessario terminare la transazione distribuita e avviarla nuovamente specificando come flag rispettivamente TMSUSPEND e TMRESUME.    // 8) sospensione della transazione distribuita    resource.end(identifier, XAResource.TMSUSPEND);

// 8 bis) effettuo degli statement fuori dalla transazione // distribuita usando lo stesso oggetto Statementstatement.executeUpdare(...);// 8 tris) riprendo la transazione distribuitaresource.start(identifier, XAResource.TMRESUME);

    

Page 101: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 101 di 182

JDBC & SSL

Page 102: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 102 di 182

Connessioni SSLPostgreSQL supporta nativamente le connessioni tramite SSL/TLS da parte dei client (si veda il capitolo 16 del manuale).

Solitamente il server PostgreSQL accetta sia connessioni in chiaro che cifrate sulla stessa porta, negoziando dinamicamente il tipo di connessione ricevuta.

Una connessione in chiaro invia i pacchetti (es. le query e i risultati) senza cifrarli, e quindi questi sono osservabili sulla rete. Una connessione SSL utilizza uno schema di cifratura per rendere illeggibili da terzi i pacchetti in transito.

Page 103: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 103 di 182

Connessioni SSL: impostazioni del server

Il server deve essere stato compilato con il supporto per SSL, e i certificati devono essere stati generati e si devono trovare nella directory $PGDATA.

Page 104: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 104 di 182

Connessioni SSL: test

Per verificare il funzionamento del supporto SSL è possibile usare il client da riga di comando.

Di default viene usata una connessione in

chiaro

La suite di cifratura è disponibile

(connessione esplicita a localhost)

Page 105: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 105 di 182

Java & SSLCi sono due modi principali di utilizzo di SSL nelle connessioni a PostgreSQL:

connessione verificata: il certificato del server è stato correttamente importato nel keystore (personale o di sistema) di Java, ad indicare che il certificato è noto e corretto. In tal caso, il driver JDBC di PostgreSQL effettua i controlli necessari per validare il certificato ed evitare attacchi man-in-the-middle.

connessione non verificata: non è possibile importare il certificato nel keystore, e quindi ci si fida al volo (con i rischi che ne derivano) del certificato presentato dal server. In questo caso occorre usare una socket factory messa a disposizione dal driver JDBC di PostgreSQL.

Page 106: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 106 di 182

Connessione verificataIl driver JDBC di PostgreSQL si occupa di tutta la parte di scambio di chiavi e connessione, ma occorre importare il certificato del server nel keystore:

(1) il certificato del server deve essere tradotto in una forma che keytool può comprendere. A tal fine lo si converte come file DER (ASN1-DER; formato senza header) dal formato PEM (standard per openssl, presenta un header testuale).

Page 107: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 107 di 182

Connessione verificata(2) il certificato deve essere importato nel keystore (in questo caso privato dell'utente). Al certificato viene associata l'alias pgday, che rappresenta una sorta di username (in realtà è il proprietario del certificato). Il keystore generato, essendo usato per la verifica di un'identità e non strettamente per la cifratura, viene anche denominato trustore.

Page 108: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 108 di 182

Connessione verificata(3) l'URL di connessione deve contenere il parametro ssl=true, al fine di indicare al driver di usare la connessione SSL (viene usata la factory socket di default).

Connection connection = DriverManager.getConnection(     "jdbc:postgresql://localhost/pgday?loglevel=2&ssl=true",        "luca",  "fluca");

(4) occorre specificare come parametri alla JVM quale keystore usare e quale password.

[luca@fluca:~]$ java ­Djavax.net.ssl.trustStore=$HOME/keystore         ­Djavax.net.ssl.trustStorePassword=aglets         it.pgday.lferrari.PgJDBC1

Page 109: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 109 di 182

Connessione verificataLa connessione impiega qualche secondo per essere convertita da normale a cifrata. Il traffico in circolazione non è più in chiaro.

Page 110: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 110 di 182

Connessione non verificataE' possible stabilire una connessione SSL senza aver verificato (importato) il certificato del server. A tal scopo è sufficiente impostare la factory di socket SSL come parametro della URL di connessione a org.postgresql.ssl.NonValidatingFactory. Non è più necessario specificare il keystore e la sua password.

Connection connection = DriverManager.getConnection(     "jdbc:postgresql://localhost/pgday?loglevel=2&ssl=true         &sslfactory=org.postgresql.ssl.NonValidatingFactory",

       "luca",  "fluca");

[luca@fluca:~]$ java it.pgday.lferrari.PgJDBC1

Page 111: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 111 di 182

org.postgresql.Driver

Page 112: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 112 di 182

Registrazione del Driver PostgreSQL

Il Driver si auto-registra in modo statico richiamando il metodo (static) registerDriver di DriverManager.

La registrazione statica anziché nel costruttore del Driver stesso (come molti alti driver fanno) impedisce una registrazione multipla dello stesso driver all'interno della JVM.

  static {        try{           java.sql.DriverManager.registerDriver(new Driver());        }        catch (SQLException e)        { e.printStackTrace(); }    }                              // org.postgresql.Driver

  static {        try{           java.sql.DriverManager.registerDriver(new Driver());        }        catch (SQLException e)        { e.printStackTrace(); }    }                              // org.postgresql.Driver

Page 113: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 113 di 182

Come il DriverManager sceglie il Driver appropriato...

private static Connection getConnection(String url, Properties info,                          ClassLoader callerCL) throws SQLException {

java.util.Vector drivers = null;...synchronized (DriverManager.class){ 

       drivers = readDrivers;     }

...SQLException reason = null;for (int i = 0; i < drivers.size(); i++) {

       DriverInfo di = (DriverInfo)drivers.elementAt(i);      Connection result = di.driver.connect(url, info);if (result != null) {    return (result);}  // codice di gestione 

      // errori ...}...

} // java.sql.DriverManager

private static Connection getConnection(String url, Properties info,                          ClassLoader callerCL) throws SQLException {

java.util.Vector drivers = null;...synchronized (DriverManager.class){ 

       drivers = readDrivers;     }

...SQLException reason = null;for (int i = 0; i < drivers.size(); i++) {

       DriverInfo di = (DriverInfo)drivers.elementAt(i);      Connection result = di.driver.connect(url, info);if (result != null) {    return (result);}  // codice di gestione 

      // errori ...}...

} // java.sql.DriverManager

class DriverInfo {    Driver         driver;    Class          driverClass;    String         driverClassName;} // java.sql.DriverManager

class DriverInfo {    Driver         driver;    Class          driverClass;    String         driverClassName;} // java.sql.DriverManager

Page 114: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 114 di 182

connect(..)

Il metodo connect(..) del driver PostgreSQL effettua la connessione effettiva al database:

si verifica che l'URL di connessione sia gestibile ed appropriato tramite il metodo acceptsURL(..);

se non è stato specificato un timeout di login viene tentata la connessione direttamente nel thread chiamante (metodo makeConnection(..));

se è stato specificato un timeout di login viene creato un nuovo thread (ConnectionThread), che cercherà di effettuare la connessione (metodo run()) mentre il thread chiamante resterà in attesa per un tempo pari al timeout specificato (metodo getResult(..)).

Page 115: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 115 di 182

connect(..)

public java.sql.Connection connect(String url, Properties info)                                             throws SQLException {

...   try {    // se non ho nessun timeout tento la connessione            // direttamente (opzione di default)            long timeout = timeout(props);            if (timeout <= 0)                return makeConnection(url, props);

  // tento la connessione tramite un altro thread            ConnectThread ct = new ConnectThread(url, props);            new Thread(ct,                  "PostgreSQL JDBC driver connection thread").start();            return ct.getResult(timeout);   } catch (PSQLException ex1) {        throw ex1;   } catch (Exception ex2) {        throw new PSQLException(...);   }} // org.postgresql.Driver

public java.sql.Connection connect(String url, Properties info)                                             throws SQLException {

...   try {    // se non ho nessun timeout tento la connessione            // direttamente (opzione di default)            long timeout = timeout(props);            if (timeout <= 0)                return makeConnection(url, props);

  // tento la connessione tramite un altro thread            ConnectThread ct = new ConnectThread(url, props);            new Thread(ct,                  "PostgreSQL JDBC driver connection thread").start();            return ct.getResult(timeout);   } catch (PSQLException ex1) {        throw ex1;   } catch (Exception ex2) {        throw new PSQLException(...);   }} // org.postgresql.Driver

Page 116: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 116 di 182

PGStream: un oggetto magico

PGStream rappresenta un wrapper attorno ad una connessione verso il database PostgreSQL.

Esso contiene un riferimento ad un oggetto java.net.Socket che rappresenta la connessione al database e agli stream per la lettura e la scrittura dei dati su tale socket (sottoclassi specifiche di OutpuStream e InputStream).

PGStream mette a disposizione dei metodi di utilità per la scrittura/lettura di byte, interi (2,4 byte), char, stringhe, etc. che vengono usati per l'invio di informazioni secondo il protocollo di comunicazione V2 o V3.

Page 117: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 117 di 182

Interfacce particolari

QueryExecutor: gestisce i messaggi specifici di una versione di protocollo (es. V3) per l'esecuzione di query. Una implmentazione è ad esempio org.postgresql.core.v3.QueryExecutorImpl.

ResultHandler: gestisce la costruzione progressiva di ResultSet (aggiunge una tupla man mano che viene ricevuta dalla rete), warning ed errori.

SimpleQuery: gestione dei dati di una query singola.

Page 118: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 118 di 182

Comunicazione FrontEnd-BackEnd

La comunicazione fra il lato applicativo (FrontEnd – driver) e il server (BackEnd) avviene tramite l'uso di un protocollo a scambio di messaggi.

Il protocollo, giunto alla sua terza revisione (PostgreSQL >= 7.4) viene comunemente denominato protocollo V3.

Page 119: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 119 di 182

Protocollo V3: formato dei messaggi

Un messaggio è un pacchetto contenente diversi byte rappresentati l'informazione.

message_type (1 byte)

length (4 bytes)

content(length – 4 bytes)

il primo byte del messaggio identifica il tipo del messaggio stesso

i successivi quattro byte specificano la lunghezza del messaggio stesso (incluso il dato di lunghezza stesso)

i rimanenti byte rappresentano il contenuto del messaggio

Page 120: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 120 di 182

PreparedStatement vs Portal

La fase di parse genera un PreparedStatement, ossia un oggetto che rappresenta l'interpretazione lessicale di una query.

Tale oggetto subirà poi il bind dei parametri venendo promosso in un Portal, un oggetto pronto ad eseguire la query.

Sostanzialmente un Portal è un handle per una query pronta da eseguire o che sta eseguendo, una sorta di cursore (che funziona anche per query non SELECT).

Un portale puo' essere identificato da un nome, oppure rimanere unnamed se si usa il portale di default.

Page 121: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 121 di 182

Protocollo V3:Simple & Extended Query

Il protocollo prevede due modalità principali di funzionamento:

Simple Query: viene inviato un messaggio con la query e questa viene immediatamente eseguita; i risultati vengono poi inviati al frontend.

Extended Query: viene inviata la query ma non viene eseguita immediatamente. In una fase successiva si fa il bind dei parametri della query, si esegue la query e si inviano i risultati al frontend.

Simple Query : Statement = Extended Query : PreparedStatement

Page 122: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 122 di 182

Simple Query

BackEndFrontEnd

Querycontiene la string SQLdella query da eseguire

RowDescriptionindica il layout delle colonne

ritornate dalla query

RowDatadati di una singola riga

RowData

RowData

ReadyForQueryindica che il backend è prontoad accettare una nuova query

Page 123: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 123 di 182

Extended Query

BackEndFrontEnd

Parse (stringa SQL, [nome prepared statement, nome portale])

ParseComplete

RowDatadati di una singola riga

RowData

RowData

ReadyForQueryil backend è pronto

Bind (lista parametri, [nome prepared statement, nome portale])

BindComplete

Execute ([nome portale])

Syncsincronizzazione per errori

Page 124: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 124 di 182

Query: overview

Viene creato un oggetto Statement dal Connection corrente. Tipicamente si passa attraverso AbstractJdbc2Connection, che richiama il tipo di connessione corretto (es. Jdbc4Connection).

Il metodo executeQuery(..) viene sempre richiamato su AbstractJdbc2Statement, che mediante QueryExecutor ottiene un oggetto SimpleQuery che implementa il protocollo di comunicazione FrontEnd e BackEnd opportuno (es. v3).

Page 125: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 125 di 182

Query: overview

Il metodo execute(..) di AbstractJdbc2Statement accetta l'oggetto query da eseguire e richiama il metodo execute(..) sul QueryExecutor. Al metodo viene anche passato un oggetto ResultHandler che si occupa di gestire risultati (ResultSet), errori e warning.

Il QueryExecutor invia i messaggi necessari al server, in particolare tramite il metodo sendQuery(..) e sendOneQuery(..) invia la query da eseguire in formato testuale.

Page 126: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 126 di 182

Query: overview

Il QueryExecutor processa la risposta del server tramite processResult(..). Questo metodo utilizza l'oggetto PGStream per leggere i messaggi in arrivo dal server. Viene usato un meccanismo ad eventi: a seconda del messaggio letto viene richiamato un metodo opportuno sul ResultHandler. In questo modo l'handler può, ad esempio, memorizzare le tuple man mano che vengono lette.

Si richiede al ResultHandler di restituire il ResultSet. Questo viene ritornato indietro lungo lo stack fino al lato applicativo.

Page 127: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 127 di 182

Creazione di uno Statement

public java.sql.Statement createStatement() throws SQLException {  // crea uno statement per un Resultset classico     return createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,                                java.sql.ResultSet.CONCUR_READ_ONLY);} // org.postgresql.jdbc2.AbstractJdbc2Connection

public java.sql.Statement createStatement() throws SQLException {  // crea uno statement per un Resultset classico     return createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,                                java.sql.ResultSet.CONCUR_READ_ONLY);} // org.postgresql.jdbc2.AbstractJdbc2Connection

public java.sql.Statement createStatement(int resultSetType,                  int resultSetConcurrency, int resultSetHoldability)                                     throws SQLException    {

// creazione di uno statement di livello 4    Jdbc4Statement s = new Jdbc4Statement(this, resultSetType,                          resultSetConcurrency, resultSetHoldability);    s.setPrepareThreshold(getPrepareThreshold());    return s;} // org.postgresql.jdbc4.Jdbc4Connection

public java.sql.Statement createStatement(int resultSetType,                  int resultSetConcurrency, int resultSetHoldability)                                     throws SQLException    {

// creazione di uno statement di livello 4    Jdbc4Statement s = new Jdbc4Statement(this, resultSetType,                          resultSetConcurrency, resultSetHoldability);    s.setPrepareThreshold(getPrepareThreshold());    return s;} // org.postgresql.jdbc4.Jdbc4Connection

La creazione di uno Statement implica ancora una volta il passaggio fra le classi dei vari livelli JDBC. In particolare si parte dal livello 2 (il minimo) e si arriva fino al livello 4 creando un Jdbc4Statement.

Page 128: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 128 di 182

Esecuzione di una query

public ResultSet executeQuery(String p_sql) throws SQLException {   // se ho una query preprata (PreparedStament) non processare   // un'altra query SQL (p_sql)   if (preparedQuery != null)       throw new PSQLException(...);

    // eseguo la query specificata    if (!executeWithFlags(p_sql, 0))        throw new PSQLException(...);

    // sono stati ritornati troppi result set in una sola volta    if (result.getNext() != null)        throw new PSQLException(...);

    return (ResultSet)result.getResultSet();} // org.postgresql.jdbc2.AbstractJdbc2Statement

public ResultSet executeQuery(String p_sql) throws SQLException {   // se ho una query preprata (PreparedStament) non processare   // un'altra query SQL (p_sql)   if (preparedQuery != null)       throw new PSQLException(...);

    // eseguo la query specificata    if (!executeWithFlags(p_sql, 0))        throw new PSQLException(...);

    // sono stati ritornati troppi result set in una sola volta    if (result.getNext() != null)        throw new PSQLException(...);

    return (ResultSet)result.getResultSet();} // org.postgresql.jdbc2.AbstractJdbc2Statement

Il metodo fondamentale per l'esecuzione di una query è executeWithFlags. Mediante tale metodo viene creato un oggetto SimpleQuery che si occuperà dello scambio di messaggi con il backend per il protocollo relativo.

Page 129: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 129 di 182

Esecuzione di una query

public boolean executeWithFlags(String p_sql, int flags)                                               throws SQLException {    // controlla se lo statement è chiuso (nel caso solleva     // una eccezione)    checkClosed();    // effettua alcune sostituzione nella query (es. escaping)    p_sql = replaceProcessing(p_sql);    Query simpleQuery =            connection.getQueryExecutor().createSimpleQuery(p_sql);    execute(simpleQuery, null, QueryExecutor.QUERY_ONESHOT | flags);    this.lastSimpleQuery = simpleQuery;    // result è un tipo ResultWrapper, che contiene     // anche il ResultSet    return (result != null && result.getResultSet() != null);} // org.postgresql.jdbc2.AbstractJdbc2Statement

public boolean executeWithFlags(String p_sql, int flags)                                               throws SQLException {    // controlla se lo statement è chiuso (nel caso solleva     // una eccezione)    checkClosed();    // effettua alcune sostituzione nella query (es. escaping)    p_sql = replaceProcessing(p_sql);    Query simpleQuery =            connection.getQueryExecutor().createSimpleQuery(p_sql);    execute(simpleQuery, null, QueryExecutor.QUERY_ONESHOT | flags);    this.lastSimpleQuery = simpleQuery;    // result è un tipo ResultWrapper, che contiene     // anche il ResultSet    return (result != null && result.getResultSet() != null);} // org.postgresql.jdbc2.AbstractJdbc2Statement

public Query createSimpleQuery(String sql) {        return parseQuery(sql, false);} // org.postgresql.core.v3.QueryExecutorImpl

public Query createSimpleQuery(String sql) {        return parseQuery(sql, false);} // org.postgresql.core.v3.QueryExecutorImpl

Page 130: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 130 di 182

Esecuzione di una query

protected void execute(Query queryToExecute,      ParameterList queryParameters, int flags) throws SQLException {     // chiusura di query precedenti e pulizia di warning ed errori     // che potevano provenire da query precedenti

    StatementResultHandler handler = new StatementResultHandler();    result = null;    connection.getQueryExecutor().execute(queryToExecute,                                          queryParameters,                                          handler,                                          maxrows,                                          fetchSize,                                          flags);    result = firstUnclosedResult = handler.getResults();} // org.postgresql.jdbc2.AbstractJdbc2Statement

protected void execute(Query queryToExecute,      ParameterList queryParameters, int flags) throws SQLException {     // chiusura di query precedenti e pulizia di warning ed errori     // che potevano provenire da query precedenti

    StatementResultHandler handler = new StatementResultHandler();    result = null;    connection.getQueryExecutor().execute(queryToExecute,                                          queryParameters,                                          handler,                                          maxrows,                                          fetchSize,                                          flags);    result = firstUnclosedResult = handler.getResults();} // org.postgresql.jdbc2.AbstractJdbc2Statement

Si prepara un handler per il risultato ed eventuali errori e si passa la query e l'handler al metodo execute(..) del QueryExecutor (la sua implementazione per il protocollo V3) affinché gestisca i messaggi.

Page 131: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 131 di 182

Esecuzione di una query (lettura risultati)

protected void processResults(ResultHandler handler, int flags)                                                 throws IOException {    Vector tuples = new Vector(); Field[] fields = null;    ...    while (!endQuery) {       c = pgStream.ReceiveChar();       switch (c){         case 'D':  // messaggio di tipo DataRow            Object tuple = null;            try {                 tuple = pgStream.ReceiveTupleV3();            } catch(OutOfMemoryError oome) { ... }

            if (!noResults)                tuples.addElement(tuple);        break;         ...

// org.postgresql.core.v3.QueryExecutorImpl

protected void processResults(ResultHandler handler, int flags)                                                 throws IOException {    Vector tuples = new Vector(); Field[] fields = null;    ...    while (!endQuery) {       c = pgStream.ReceiveChar();       switch (c){         case 'D':  // messaggio di tipo DataRow            Object tuple = null;            try {                 tuple = pgStream.ReceiveTupleV3();            } catch(OutOfMemoryError oome) { ... }

            if (!noResults)                tuples.addElement(tuple);        break;         ...

// org.postgresql.core.v3.QueryExecutorImpl

Page 132: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 132 di 182

Esecuzione di una query (lettura risultati)

     case 'C':  // command status (fine esecuzione execute)       String status = receiveCommandStatus();       ...       if (fields != null || tuples != null) {         handler.handleResultRows(currentQuery, fields, tuples, null);        fields = null;  tuples = null;       }       else         interpretCommandStatus(status, handler);       ...       break;     case 'Z':    // ready for query        receiveRFQ();        endQuery = true;   // termina il ciclo while        // pulizia degli oggetti in memoria e delle code        ...        break;     ...   }  }} // org.postgresql.core.v3.QueryExecutorImpl

     case 'C':  // command status (fine esecuzione execute)       String status = receiveCommandStatus();       ...       if (fields != null || tuples != null) {         handler.handleResultRows(currentQuery, fields, tuples, null);        fields = null;  tuples = null;       }       else         interpretCommandStatus(status, handler);       ...       break;     case 'Z':    // ready for query        receiveRFQ();        endQuery = true;   // termina il ciclo while        // pulizia degli oggetti in memoria e delle code        ...        break;     ...   }  }} // org.postgresql.core.v3.QueryExecutorImpl

Page 133: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 133 di 182

Query: traffico di rete

Analizzando il traffico di rete con uno sniffer è possibile vedere i singoli messaggiinviati da e per il server. Nell'esempio qui sopra si nota il messaggio di Parse,con la query inviata, seguito da vari messaggi uno dei quali Execute.

Page 134: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 134 di 182

Query: traffico di rete

In questo caso si vede il messaggio RowDescription che precede una serie di messaggi DataRow, ciascuno con indicazione del numero, lunghezza e valore

delle colonne.

Page 135: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 135 di 182

Query: traffico di rete

Messaggio Parse contenente la query e il nome dello statement di riferimento sul server (null).

Il server invia un RowDescription e di seguito tutte le righe tramite una serie di

DataRow.

Il server invia i messaggi di chiusura, fra i quali ReadyForQuery.

Page 136: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 136 di 182

Esecuzione di una query parametrica

public void setInt(int parameterIndex, int x) throws SQLException {   checkClosed();            bindLiteral(parameterIndex, Integer.toString(x), Oid.INT4);}

private void bindLiteral(int paramIndex,String s,int oid)                                               throws SQLException {   if(adjustIndex)      paramIndex­­;   // registro il parametro in un contenitore V3ParameterList   preparedParameters.setLiteralParameter(paramIndex, s, oid);}                   // org.postgresql.jdbc2.AbstractJdbc2Statement

public void setInt(int parameterIndex, int x) throws SQLException {   checkClosed();            bindLiteral(parameterIndex, Integer.toString(x), Oid.INT4);}

private void bindLiteral(int paramIndex,String s,int oid)                                               throws SQLException {   if(adjustIndex)      paramIndex­­;   // registro il parametro in un contenitore V3ParameterList   preparedParameters.setLiteralParameter(paramIndex, s, oid);}                   // org.postgresql.jdbc2.AbstractJdbc2Statement

statement2.setInt(1, corsoPK);      // lato applicativostatement2.setInt(1, corsoPK);      // lato applicativo

La fase di bind in memoria prevede che il parametro sia associato alla sua posiziona nella query (index), al suo valore (rappresentato come stringa) e al suo tipo (OID) affinché il BackEnd possa capire come trattare il dato stesso.I parametri sono contenuti in oggetti che implementano ParameterList a seconda del protocollo utilizzato (ad es. V3ParameterList).

Page 137: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 137 di 182

Esecuzione di una query parametrica

public java.sql.ResultSet executeQuery() throws SQLException {  if (!executeWithFlags(0))            throw new PSQLException(...);

  if (result.getNext() != null)            throw new PSQLException(...);

  return (ResultSet) result.getResultSet();}                   // org.postgresql.jdbc2.AbstractJdbc2Statement

public java.sql.ResultSet executeQuery() throws SQLException {  if (!executeWithFlags(0))            throw new PSQLException(...);

  if (result.getNext() != null)            throw new PSQLException(...);

  return (ResultSet) result.getResultSet();}                   // org.postgresql.jdbc2.AbstractJdbc2Statement

Il metodo executeQuery(), invocato dopo il bind dei parametri, richiama il metodo executeWithFlags(..) già visto in precedenza e usato anche per query non parametriche. Ripercorrendo lo stack di chiamata si giunge a QueryExecutor.execute(..) che questa volta ha impostato i parametri della query. Da qui si passa poi a QueryExecutor.sendOneQuery(..) che provvede a sostituire ad ogni parametro (indicato con ?) un $n (con n valore della posizione partendo da 1). In coda alla query compare poi l'array degli OID dei tipi dei parametri.

FE=> Parse(stmt=null,query="SELECT cognome, nome                 FROM partecipante WHERE corsoPK=$1",oids={23})

FE=> Parse(stmt=null,query="SELECT cognome, nome                 FROM partecipante WHERE corsoPK=$1",oids={23})

Page 138: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 138 di 182

Esecuzione di una query parametrica

 private void sendBind(SimpleQuery query, SimpleParameterList params,                                 Portal portal) throws IOException {    // informazioni su portale e statement (se esistono)    String statementName = query.getStatementName();    byte[] encodedStatementName = query.getEncodedStatementName();    byte[] encodedPortalName = (portal == null ? null :                                    portal.getEncodedPortalName());

    ...    pgStream.SendChar('B');                   // messaggio Bind    pgStream.SendInteger4((int)encodedSize);  // dimensione messaggio    if (encodedPortalName != null) // eventuale portale        pgStream.Send(encodedPortalName);        pgStream.SendChar(0);                       if (encodedStatementName != null)        pgStream.Send(encodedStatementName);  // eventuale statement    pgStream.SendChar(0);                    

    ...                   // org.postgresql.core.v3.QueryExecutorImpl

 private void sendBind(SimpleQuery query, SimpleParameterList params,                                 Portal portal) throws IOException {    // informazioni su portale e statement (se esistono)    String statementName = query.getStatementName();    byte[] encodedStatementName = query.getEncodedStatementName();    byte[] encodedPortalName = (portal == null ? null :                                    portal.getEncodedPortalName());

    ...    pgStream.SendChar('B');                   // messaggio Bind    pgStream.SendInteger4((int)encodedSize);  // dimensione messaggio    if (encodedPortalName != null) // eventuale portale        pgStream.Send(encodedPortalName);        pgStream.SendChar(0);                       if (encodedStatementName != null)        pgStream.Send(encodedStatementName);  // eventuale statement    pgStream.SendChar(0);                    

    ...                   // org.postgresql.core.v3.QueryExecutorImpl

Page 139: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 139 di 182

Esecuzione di una query parametrica

    // invio tipo e numero dei parametri    for (int i = 1; i <= params.getParameterCount(); ++i)        pgStream.SendInteger2(params.isBinary(i) ? 1 : 0);     pgStream.SendInteger2(params.getParameterCount());          ...    for (int i = 1; i <= params.getParameterCount(); ++i)  {       if (params.isNull(i))          pgStream.SendInteger4( ­1);     // dimensione ­1 => NULL       else {          // dimensione del parametro          pgStream.SendInteger4(params.getV3Length(i));             try{ // valore del parametro              params.writeV3Value(i, pgStream);                           }catch (PGBindException be) {                    bindException = be;          }       }   }   ...}                   // org.postgresql.core.v3.QueryExecutorImpl

    // invio tipo e numero dei parametri    for (int i = 1; i <= params.getParameterCount(); ++i)        pgStream.SendInteger2(params.isBinary(i) ? 1 : 0);     pgStream.SendInteger2(params.getParameterCount());          ...    for (int i = 1; i <= params.getParameterCount(); ++i)  {       if (params.isNull(i))          pgStream.SendInteger4( ­1);     // dimensione ­1 => NULL       else {          // dimensione del parametro          pgStream.SendInteger4(params.getV3Length(i));             try{ // valore del parametro              params.writeV3Value(i, pgStream);                           }catch (PGBindException be) {                    bindException = be;          }       }   }   ...}                   // org.postgresql.core.v3.QueryExecutorImpl

Page 140: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 140 di 182

Esecuzione di INSERT/UPDATEI passi fondamentali sono simili a quanto visto in precedenza:

si deve ottenere dall'oggetto Connection (AbstractJdbc2Connection) uno Statement (AbstractJdbc2Statement);

si richiede allo Statement (AbstractJdbc2Statement) di eseguire la modifica mediante il metodo JDBC executeUpdate(String sql);

si ottiene come valore di ritorno il numero di record modificati nella base di dati (command status)

Il command status viene interpretato letteralmente, ossia dalla stringa di command status inviata dal BackEnd si estraggono i valori ritornati dal server.

Page 141: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 141 di 182

Esecuzione di un INSERT/UPDATE

public int executeUpdate(String p_sql) throws SQLException  {    ...    // eseguo la query tramite il QueryExecutor    // non mi aspetto risultati (QUERY_NO_RESULTS)    if (executeWithFlags(p_sql, QueryExecutor.QUERY_NO_RESULTS))        // non dovrei avere alcun risultato        throw new PSQLException(...);

    // restituisco il numero di record aggiornati/inseriti    return getUpdateCount();}                // org.postgresql.jdbc2.AsbtractJdbc2Statement

public int executeUpdate(String p_sql) throws SQLException  {    ...    // eseguo la query tramite il QueryExecutor    // non mi aspetto risultati (QUERY_NO_RESULTS)    if (executeWithFlags(p_sql, QueryExecutor.QUERY_NO_RESULTS))        // non dovrei avere alcun risultato        throw new PSQLException(...);

    // restituisco il numero di record aggiornati/inseriti    return getUpdateCount();}                // org.postgresql.jdbc2.AsbtractJdbc2Statement

 public boolean executeWithFlags(String p_sql, int flags)                              throws SQLException  {   Query simpleQuery =             connection.getQueryExecutor().createSimpleQuery(p_sql);   execute(simpleQuery, null, QueryExecutor.QUERY_ONESHOT | flags);   this.lastSimpleQuery = simpleQuery;   return (result != null && result.getResultSet() != null);}          // org.postgresql.jdbc2.AsbtractJdbc2Statement

 public boolean executeWithFlags(String p_sql, int flags)                              throws SQLException  {   Query simpleQuery =             connection.getQueryExecutor().createSimpleQuery(p_sql);   execute(simpleQuery, null, QueryExecutor.QUERY_ONESHOT | flags);   this.lastSimpleQuery = simpleQuery;   return (result != null && result.getResultSet() != null);}          // org.postgresql.jdbc2.AsbtractJdbc2Statement

Page 142: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 142 di 182

Esecuzione di un INSERT/UPDATE

protected void processResults(ResultHandler handler, int flags)                                                throws IOException {   ....                                           case 'C':  // Command Status (end of Execute)      // ottiene la stringa risultato dell'esecuzione della query      // ad esempio INSERT 0 1 oppure UPDATE 1      String status = receiveCommandStatus();      if (fields != null || tuples != null){          // qui c'e' un ResultSet, quindi una select           handler.handleResultRows(currentQuery, fields, tuples, null);        fields = null;  tuples = null;      }      else {       // qui non c'e' ResultSet, quindi una scrittura sul DB       interpretCommandStatus(status, handler);      }    ...}                // org.postgresql.core.v3.QueryExecutorImpl

protected void processResults(ResultHandler handler, int flags)                                                throws IOException {   ....                                           case 'C':  // Command Status (end of Execute)      // ottiene la stringa risultato dell'esecuzione della query      // ad esempio INSERT 0 1 oppure UPDATE 1      String status = receiveCommandStatus();      if (fields != null || tuples != null){          // qui c'e' un ResultSet, quindi una select           handler.handleResultRows(currentQuery, fields, tuples, null);        fields = null;  tuples = null;      }      else {       // qui non c'e' ResultSet, quindi una scrittura sul DB       interpretCommandStatus(status, handler);      }    ...}                // org.postgresql.core.v3.QueryExecutorImpl

Page 143: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 143 di 182

Pl/Java

Page 144: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 144 di 182

Installazione di Pl/JavaPljava si basa su un modulo del kernel denominato pljava.so; la versione base è compilata per architettura 32 bit e verso PostgreSQL 8.3. Per una corretta installazione su piattaforme e database differenti occorre:usare un JDK fra la versione 4 e 5 (non la 6 perché cambiano le API JDBC);

installare gli header per lo sviluppo di PostgreSQL della versione corrente (ad esempio postgresql­server­dev­8.4);

scaricare tramite CVS l'ultima versione dei sorgenti di Pl/Java;

verificare che JAVA_HOME e il PATH puntino al compilatore Java corretto;

lanciare la compilazione con il comando make.

Page 145: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 145 di 182

Installazione di Pl/Java

Occorre poi apportare alcune modifiche a postgresql.conf affinché il server-cluster postmaster possa caricare l'estensione Java:

dynamic_library_path = '$libdir:/sviluppo/java/pljava/org.postgresql.pljava/build/objs'

custom_variable_classes = 'pljava'

pljava.classpath='/sviluppo/java/pljava/org.postgresql.pljava/build/pljava.jar'

Page 146: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 146 di 182

Introduzione a Pl/JavaPl/Java è una estensione di PostgreSQL per supportare il linguaggio Java come linguaggio interno.

Pl/Java è simile, concettualmente, ad altri Pl/xxx come ad esempio Pl/Perl. La differenza principale con altri linguaggi è che, essendo Java compilato, non è possibile scrivere direttamente codice Java all'interno del server, bensì si deve istruire il server per richiamare del codice Java in formato bytecode. In altre parole usare Pl/Java significa sempre:

Scrivere del codice Java.

Scrivere del codice SQL per richiamare Java.

Page 147: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 147 di 182

JNI?Il backend PostgreSQL non è scritto in Java! Occorre quindi trovare un modo per far dialogare un pezzo di codice Java con il backend. Ci sono due soluzioni possibili:

RPC

JNI

Pl/Java si basa su JNI per svariate ragioni, principalmente:

Piu' semplice ed efficiente

Il codice Java risiede sul server, quindi è locale al backend (non c'è necessità di usare chiamate remote)

Page 148: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 148 di 182

1 backend = 1 JVM ?

// backend.cstatic void initializeJavaVM(void){

...jstat = JNI_createVM(&s_javaVM, &vm_args);

   ...}

Nel file backend.c, che inizializza il collegamento fra il backend vero e proprio e Java, si ha che per ogni connessione viene avviata una JVM tramite JNI.In sostanza si ha una JVM per ogni connessione utente.Ciò è coerente con il design di PostgreSQL che prevede un processo (backend appunto) per ogni connessione utente, inoltre garantisce la protezione dello spazio utente tramite l'astrazione dei processi e infine consente di gestire la priorità dei processi stessi tramite gli strumenti del sistema operativo (ps, nice, ...).

Page 149: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 149 di 182

Installazione di Pl/Java

PlJava viene fornito con uno strumento (Java) per l'installazione del supporto Java presso un determinato database: deploy.jar. Occorre eseguire questo programma Java (che richiede la presenza del driver JDBC PostgreSQL) per installare il supporto dinamico a Java nel database:

java  org.postgresql.pljava.deploy.Deployer ­install ­database hrpmdb ­user postgres ­password postgres

Il programma provvede a creare le funzioni handler e lo schema speciale sqlj nel database.

Page 150: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 150 di 182

Vedere cosa succede dietro le quinte

E' bene abilitare il livello di log_min_messages a debug3 per ottenere un po' di informazioni circa l'utilizzo di Java nel server. Nei log si troveranno messaggi circa la creazione dell'istanza della JVM e il caricamento delle classi.2010­06­01 13:55:03 CEST DEBUG:  find_in_dynamic_libpath: trying "/usr/lib/postgresql/8.4/lib/pljava"2010­06­01 13:55:03 CEST DEBUG:  find_in_dynamic_libpath: trying "/sviluppo/java/pljava/org.postgresql.pljava/build/objs/pljava"2010­06­01 13:55:03 CEST DEBUG:  find_in_dynamic_libpath: trying "/usr/lib/postgresql/8.4/lib/pljava.so"2010­06­01 13:55:03 CEST DEBUG:  find_in_dynamic_libpath: trying "/sviluppo/java/pljava/org.postgresql.pljava/build/objs/pljava.so"2010­06­01 13:55:03 CEST DEBUG:  Using integer_datetimes2010­06­01 13:55:03 CEST DEBUG:  Added JVM option string "­Djava.class.path=/sviluppo/java/pljava/org.postgresql.pljava/build"2010­06­01 13:55:03 CEST DEBUG:  Added JVM option string "­Dsqlj.defaultconnection=jdbc:default:connection"2010­06­01 13:55:03 CEST DEBUG:  Added JVM option string "vfprintf"2010­06­01 13:55:03 CEST DEBUG:  Added JVM option string "­Xrs"2010­06­01 13:55:03 CEST DEBUG:  Creating JavaVM2010­06­01 13:55:03 CEST DEBUG:  JavaVM created2010­06­01 13:55:03 CEST DEBUG:  Getting Backend class pljava.jar

Page 151: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 151 di 182

Un primo esempio di funzione

CREATE FUNCTION getsysprop(VARCHAR)  RETURNS VARCHAR  AS 'java.lang.System.getProperty'  LANGUAGE java;

Page 152: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 152 di 182

Scrivere funzioni PL/JavaOgni metodo invocabile in PostgreSQL deve essere un metodo statico (i parametri devono corrispondere a quelli passati alla funzione SQL).

Ogni funzione Java deve essere invocata tramite una funzione SQL.

Le classi devono essere contenute in un file jar caricato e impostato nel classpath del database.

Il classpath viene gestito solo dagli utenti amministratori.

ATTENZIONE: se vengono riportate eccezioni di sicurezza significa che la classe è presente nel classpath del processo postmaster e quindi viene caricata senza passare dal motore

database!

Page 153: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 153 di 182

Un secondo esempio di funzione

Page 154: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 154 di 182

Parametri e tipi di ritorno: un esempio

Page 155: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 155 di 182

NULL vs null

Il tipo NULL del linguaggio SQL viene tradotto nel tipo null di Java.

Però per SQL è lecito avere NULL anche dove c'è uno scalare, mentre per Java no!

Di conseguenza le funzioni che lavorino con scalari (in ingresso/uscita) e che debbano gestire tipi NULL (SQL) devono usare le classi wrapper.

Page 156: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 156 di 182

Funzioni che restituiscono un SETOFPl/Java richiede che una funzione che ritorna un SETOF (lato SQL) restituisca un Iterator (lato Java). Non verranno accettati altri tipi di ritorno!

E' comunque possibile usare un qualunque tipo di dato/struttura che implementi l'interfaccia Iterator.

Page 157: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 157 di 182

Creazione di TriggerIl codice Java dipende dalle librerie Pl/Java

Ogni funzione trigger non restituisce nulla (void) e accetta come parametro un oggetto TriggerData con le informazioni sull'invocazione del Trigger.

Tramite TriggerData è possibile selezionare il result set new oppure old e su questi agire (old è in sola lettura).

Page 158: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 158 di 182

Esempio di TriggerSi vuole creare un trigger che modifichi una stringa di testo con le meta-informazioni sul trigger stesso.

Page 159: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 159 di 182

Esempio di Trigger

Page 160: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 160 di 182

Esempio piu' complesso di TriggerSi supponga di voler tenere traccia del numero globali di invocazioni della funzione trigger, del numero di update di una riga e di impedire le cancellazioni delle righe che sono state aggiornate un numero pari di volte.

Page 161: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 161 di 182

Codice della funzione Triggerpublic class Trigger {

    public static int globalCounter = 0;        public static void triggerJavaMethod( TriggerData triggerData )                                                     throws SQLException{

// e' un trigger per update?if( triggerData.isFiredByUpdate() ){

          // prelevo il result set nuovo     ResultSet newRS = triggerData.getNew();     // prelevo il vecchio result set     ResultSet oldRS = triggerData.getOld();     // inserisco il contatore globale     newRS.updateInt("global_counter", ++globalCounter);     // incremento il contatore di update     newRS.updateInt("update_counter", 

                                        oldRS.getInt("update_counter") + 1);}else if( triggerData.isFiredByDelete() ){

     ResultSet oldRS = triggerData.getOld();     if( ( oldRS.getInt("update_counter") % 2 ) == 0 )

throw new TriggerException(triggerData, "Tupla non ..");}

   }

Non esiste ancora un metodo efficace per gestire l'abort di un trigger!Lanciare una eccezione non funziona appieno: blocca la transazione nel backend!

Page 162: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 162 di 182

Esecuzione del Trigger

Page 163: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 163 di 182

Maneggiare tipi complessiI tipi complessi devono essere trattati tramite un ResultSet aggiornabile.

Si rinuncia al paradigma OOP per passare ad un paradigma in stile record-based.

E' comodo usare un ResultSetGenerator:

La funzione (lato SQL) richiama un metodo (lato Java) statico che restituisce un oggetto che implementa l'interfaccia ResultSetProvider.

ResultSetProvider contiene due metodi: assignRowValues(..) che permette l'update di una cella (riga/colonna) nel ResultSet e close() usato per rilasciare le risorse.

ResultSetProvider.assignRowValues(..) restituisce false se non ci sono altre righe da processare, true se ancora una riga deve essere inserita nel ResultSet.

Page 164: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 164 di 182

Maneggiare tipi complessi: generare delle tuplepublic class RowGenerator implements ResultSetProvider {

    private static int NUM_ROWS = 10;    private static int NUM_COLS = 5;        public boolean assignRowValues(ResultSet rs, int rowNumber)

    throws SQLException {

if( rowNumber <= NUM_ROWS ){    for( int j = 1; j <= NUM_COLS; j++ )

rs.updateString(j, "Riga " + rowNumber + " Colonna " + j);        return true;}else    return false;

    }

    public void close() throws SQLException {System.out.println("Chiusura del row set provider");

    }        public static ResultSetProvider generateRows(){

return new RowGenerator();    }

}

Page 165: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 165 di 182

Maneggiare tipi complessi: invocazioni SQL

Page 166: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 166 di 182

JDBC & Pl/Java =~ SQL MED

E' possibile chiamare JDBC da Pl/Java, in modo da poter interrogare in modo riflessivo il database al quale si è connessi.In questo modo si possono ottenere (e restituire) i dati appartenenti ad altre tabelle/relazioni.

Page 167: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 167 di 182

JDBC & Pl/Java =~ SQL MED

public class RowGenerator implements ResultSetHandle {

    private String databaseName;    private String username;    private String password;    private String tableName;            public RowGenerator(String database, String username,                                        String password, String table ){

this.databaseName = database;this.username = username;this.password = password;this.tableName = table;

    }        // funzione usata lato SQL    public static ResultSetHandle generateRows(String database, String username,                                                String password, String table){

return new RowGenerator( database, username, password, table );    }

Page 168: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 168 di 182

JDBC & Pl/Java = SQL MED

// funzione usata per ottenere le tuple    public ResultSet getResultSet() throws SQLException {

org.postgresql.Driver driver = new org.postgresql.Driver();

Connection connection = DriverManager.getConnection( this.databaseName, this.username, this.password );

Statement statement   = connection.createStatement();return statement.executeQuery(" SELECT * FROM "+  this.tableName );

    }

}

Se la funzione viene creata come linguaggio untrusted allora è possibile usare la connessione verso ogni database, e quindi implementare un SQL MED completo.

ATTENZIONE: i driver devono trovarsi nel classpath!

Page 169: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 169 di 182

Utilizzo dei SavePointpublic static void executeInsert(int counter) throws SQLException{

Connection connection = DriverManager.getConnection("jdbc:default:connection");

Statement statement   = connection.createStatement();

Savepoint save = null;

// effettuo 10 inserimentifor( int i = 0; i < counter; i++ ){    statement.execute("INSERT INTO java_table(nome) 

                                VALUES('inserimento­" + i + "')");    if( i >= (counter / 2) && save == null )

save = connection.setSavepoint();}

// rollback connection.rollback( save );

    }

Page 170: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 170 di 182

SavePoint: risultato

Page 171: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 171 di 182

Listener    private static Logger logger = Logger.getAnonymousLogger();    public static void executeInsert(int counter) throws SQLException{

Connection connection = DriverManager.getConnection("jdbc:default:connection");

Session session = SessionManager.current();session.addSavepointListener( new SavepointListener() {

        public void onStart(Session arg0, Savepoint arg1, Savepoint arg2)

    throws SQLException {logger.info("Savepoint START " + arg0 + " savepoints " + arg1 + " " 

+ arg2);    }        public void onCommit(Session arg0, Savepoint arg1, Savepoint arg2)

    throws SQLException {logger.info("Savepoint COMMIT" + arg0 + " savepoints " + arg1 + " " 

+ arg2);    }        public void onAbort(Session arg0, Savepoint arg1, Savepoint arg2)

    throws SQLException {logger.info("Savepoint ABORT " + arg0 + " savepoints " + arg1 + " " 

+ arg2);

    }});

Page 172: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 172 di 182

Listener

E' possibile agganciare dei listener per le transazioni (TransactionListener) e per la gestione dei SavePoint (sotto-transazioni) (SavePointListener).

Page 173: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 173 di 182

JDBC & Pl/Java =~ SQL MEDpublic class RowGenerator implements ResultSetHandle {    private String databaseName;    private String username;    private String password;    private String tableName;        public RowGenerator(String database,           String username, String password, String table ){

this.databaseName = database;this.username = username;this.password = password;this.tableName = table;

    }            public static ResultSetHandle generateRows(String database,   String username, String password, String table){

return new RowGenerator( database, username, password, table );    }

    public ResultSet getResultSet() throws SQLException {Connection connection = DriverManager.getConnection( this.databaseName, 

this.username, this.password );Statement statement   = connection.createStatement();return statement.executeQuery(" SELECT * FROM "+  this.tableName );

    }

}

Page 174: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 174 di 182

Singleton e PooledObjectsPl/Java consente la gestione di oggetti in un pool, ovvero un recipiente di oggetti che possono essere riciclati per scopi futuri. In un certo senso questo aiuta nel paradigma dei singleton.

Ogni oggetto che deve essere gestibile da un pool deve implementare l'interfaccia PooledObject e implementare i metodi per la attivazione e disattivazione.

Il costruttore dell'oggetto deve accettare il pool di appartenenza (ObjectPool) sul quale puo' operare.

Page 175: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 175 di 182

Esempio di PooledObjectpublic class Pooled implements PooledObject{        private long creationTimeMillis;    private ObjectPool myPool;        public Pooled( ObjectPool myPool ){

super();this.creationTimeMillis = 

Calendar.getInstance().getTimeInMillis();this.myPool = myPool;

    }

    public void activate() throws SQLException {System.out.println("Oggetto <" + creationTimeMillis + "> 

attivato!");    }

    public void passivate() throws SQLException {System.out.println("Oggetto <" + creationTimeMillis + "> 

disattivato!");    }

Page 176: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 176 di 182

Esempio di PooledObject    public void remove() {

System.out.println("Oggetto <" + creationTimeMillis + "> rimosso!");

    }        public static void createPooledObject() throws SQLException{

// ottengo la sessione correnteSession session = SessionManager.current();// ottengo il pool di oggettiObjectPool pool = session.getObjectPool( Pooled.class );// ottengo una istanza dal poolPooled myPooled = (Pooled) pool.activateInstance();// ci faccio qualcosa....

// poi lo reinserisco nel poolpool.passivateInstance(myPooled);

    }

}

Page 177: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 177 di 182

Esempio di PooledObject

Se si invoca ripetutamente la funzione di generazione dell'oggetto si nota che è sempre lo stesso oggetto ad essere usato.

Se l'oggetto non viene reinserito nel pool, allora un nuovo viene creato e usato.

Page 178: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 178 di 182

MultithreadingProblema: Java è un linguaggio che supporta il multi-threading, ma il backend PostgreSQL è un singolo processo senza supporto ai thread.

Il supporto al multithreading di Java deve essere comunque garantito!

Se si pensa al backend come ad un ennesimo thread, allora tutti i thread (Java e di backend) devono sincronizzarsi in modo coerente: si utilizza un singolo lock!

Viene definito un oggetto particolare, il Backend.THREADLOCK sul quale tutti si sincronizzano.

Page 179: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 179 di 182

Backend.THREADLOCK

Page 180: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 180 di 182

Funzionamento del lock

L'idea è semplice: il lock funziona come un mutex.

Il processo backend detiene il lock dall'inizio.

Quando il processo chiama una funzione Java (ad esempio perché scatta un trigger) rilascia il lock.

La funzione Java acquisisce il lock.

Quando la funzione Java termina il backend riacquisisce il lock.

Così facendo il lock garantisce che ci sia sempre e solo un flusso di esecuzione: o il backend è in attesa della terminazione di Java oppure Java è in attesa della terminazione di un flusso di backend.

Page 181: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 181 di 182

Funzionamento del lock

BackendBackend JavaJava

SELECT example1();

Esecuzione di una query

risultati

<fine>

Page 182: Java & PostgreSQL: da JDBC a Pl/Java - confsl.org€¦ · Java & PostgreSQL – Luca Ferrari 2 di 182 Synopsis Questa presentazione vuole fungere da punto di riferimento per gli sviluppatori

Java & PostgreSQL – Luca Ferrari 182 di 182

Utilizzo del lock

 public int fetch(boolean forward, int count)  throws SQLException  {            synchronized(Backend.THREADLOCK) {                                   return _fetch(m_pointer,             System.identityHashCode(                   Thread.currentThread()), forward, count);        }  

Tutti i metodi della classe Portal.java (e delle classi che rappresentano oggetti del backend) utilizzano il lock come prima istruzione al fine di ottenere il lock dal backend. Ovviamente il lock va acquisito qualora si debba procedere con un metodo nativo.