Array
d'oggetti
L'uso
di semplici oggetti così definiti é
sicuramente utile, ma ancora di più lo é
la possibilità di usare array d'oggetti. In
Oracle esistono due tipi d'array, le "Nested
Table" ed i "Varray": i primi sono
array d'oggetti la cui dimensione massima non é
specificata, mentre i secondi sono array a grandezza
massima fissa ("bounded array"), definita
all'atto di dichiarazione dell'array.
In quest'articolo ci occuperemo solo dei primi, più
adatti al nostro scopo.
La dichiarazione di un array in Oracle avviene in
modo seguente:
CREATE
OR REPLACE TYPE A_EMPLOYEE AS TABLE OF O_EMPLOYEE;
Non
ci si lasci ingannare dall'uso della parola Table:
questo comando DDL non crea nessuna tabella, é
solo la dichiarazione di una collezione o di un vettore,
di lunghezza teoricamente illimitata, composto da
oggetti di tipo O_EMPLOYEE.
L'uso
di un tal elemento, é abbastanza semplice (si
veda a riguardo anche la sezione Resources), ma richiede
l'uso di classi proprietarie del fornitore del driver
JDBC, nel nostro caso Oracle.
Nel codice fornito, si guardi ad esempio la classe
'SetEmployees.java':
EmployeeWrap[]
wraps = new EmployeeWrap[5];
[...]
// registrazione parametri di IN ed OUT
oracle.sql.ArrayDescriptor emps_ard = new oracle.sql.ArrayDescriptor(type,
conn);
oracle.sql.ARRAY emps_array = new oracle.sql.ARRAY(emps_ard,
conn, wraps);
((oracle.jdbc.driver.OracleCallableStatement)statement).setArray(2,
emps_array);
Nelle
precedenti righe di codice creato un array della classe
EmployeeWrap, e tramite questo un ArrayDescriptor,
ovvero una classe d'utilità proprietaria di
Oracle. Da questa si costruisce un oggetto ARRAY che
é usato come normale Input Parameter di una
Stored Procedure, senza avere alcun bisogno di un
nuovo Wrapper: si occupa il driver di gestire la mappatura
in maniera trasparente al programmatore.
Dall'analisi dello spezzone di codice si può
vedere come l'oggetto ARRAY sia costruito a partire
da un array di Wrapper, essendo quest'ultima classe
un tipo a tutti gli effetti, e da una connessione,
in quanto il driver deve poter leggere dal database
la definizione dell'oggetto SQL3 corrispondente per
preparare un adeguato SQL stream.
Attenzione ad una cosa: il driver riporta un errore
se l'oggetto Connection non é implementato
con una classe OracleConnection o da essa derivata.
Questo può avvenire qualora si usi un ConnectionPool
tramite l'interfaccia DataSource (come é corretto
fare all'interno di un Application Server): si riesce
ad ovviare facilmente all'inconveniente andando ad
estendere questa classe (com.beasys.wle.jdbc.driver.Connection
in BEA WebLogic Enterprise Server) e riportando l'originale
OracleConnection, che essendo un membro protetto della
classe summenzionata é accessibile dalla classe
che lo estende.
Chiaramente
gli Array possono essere anche creati a partire da
primitive, come ad esempio i long ed i loro corrispondenti
Number nel DB:
CREATE
OR REPLACE TYPE A_NUMBER AS TABLE OF NUMBER(7);
In
modo del tutto analogo a quanto fatto precedentemente,
possiamo usare l'array di number come un normale tipo
di dato. Nella letteratura un tipo strutturato (ovvero
un oggetto) che abbia solo un attributo basato su
di un tipo di dato primitivito, é indicato
come "Distinct Data Type".
Sempre dall'esempio precedentemente citato:
statement.registerOutParameter(1,
oracle.jdbc.driver.OracleTypes.ARRAY,
"A_NUMBER");
statement.execute();
// legge la Primary Key assegnata all'oggetto
ResultSet rs = statement.getArray(1).getResultSet();
while (rs.next())
System.out.println("PKEY =
" + rs.getLong(1));
Riguardo
all'uso di Collections in PL/SQL, consiglio di guardare
la documentazione di Oracle9i, citata nel paragrafo
Resources. Analizzando brevemente la Stored Procedure
setEmployee
PKEYS
A_NUMBER := A_NUMBER();
[...]
FOR i IN 1..EMPLOYEES.COUNT LOOP
PKEYS.extend();
PKEYS(PKEYS.COUNT):= SET_EMPLOYEE(EMPLOYEES(i));
Qui
vediamo come gli array siano riferiti per indice,
ed hanno metodi per essere manipolati.
Per maggiori informazioni si veda la documentazione
di Oracle riportata nel paragrafo Resources a fine
articolo.
Oggetti Composti
Il naturale passo successivo dopo aver visto oggetti
semplici, ovvero i cui attributi sono delle primitive,
e gli array d'oggetti é quello di analizzare
un oggetto complesso che contenga anche attributi
che non siano delle semplici primitive. Introduciamo
quindi la definizione di un oggetto di tipo Department
che si trova in relazione '1 a molti' con l'oggetto
Employee.
La dichiarazione del corrispondente oggetto SQL3 é
la seguente:
CREATE
OR REPLACE TYPE O_DEPARTMENT AS OBJECT (
DEPTNO NUMBER (2),
DNAME VARCHAR2 (14),
LOC VARCHAR2 (13),
EMPS A_EMPLOYEE
);
Come
si nota, l'attributo 'emps' é di tipo A_EMPLOYEE.
Per usare questo nuovo tipo di dato necessitiamo,
come abbiamo visto nel precedente articolo, di un
Wrapper, ovvero di una classe che si occupi di rappresentare
in ambiente Java l'oggetto SQL3. La classe 'DepartmentWrap'
é del tutto analoga agli altri Wrapper, differisce
solo nel modo di lettura e di scrittura dell'array:
java.util.ArrayList
_list = new java.util.ArrayList();
java.sql.Array array;
Object[] objs;
int i;
array = stream.readArray();
objs = (Object[])array.getArray(OrmTest.getInstance().getMap());
_list.ensureCapacity(objs.length);
for (i = 0; i < objs.length; i++){
try{
_list.add(((EmployeeWrap)objs[i])._obj);
}
catch (NullPointerException e){
System.out.println("JDBC
PROBLEM : " + e.getMessage());
}
}
Employee[] emps = (Employee[])_list.toArray(new Employee[0]);
_obj = new Department(depno, depname, loc, emps);
Il
metodo readArray dell'interfaccia SQLInputStream consente
di leggere un Array, il cui tipo base dev'essere contenuto
nella Type Map affinché il driver sia in grado
di identificarlo.
Gi array sono supportati in maniera pienamente utilizzabile
solo dalla versione 8.1.7 di Oracle: la versione 8.1.6
del thin Driver ha un baco per cui un array di dimensione
N viene riportato come un array di dimensione N+1
dove il primo elemento é uguale a "null".
Attenzione inoltre alle compatibilità tra Driver
e Database: se le versioni sono diverse, lo stream
potrebbe non essere costruito correttamente.
Vediamo adesso come lo stesso Wrapper scriva un oggetto
di tipo Department ed il relativo Array di Employee
nel DB:
Department
obj = (Department)_obj;
stream.writeLong(obj.depno);
stream.writeString(obj.depname);
stream.writeString(obj.loc);
EmployeeWrap[] vars_wraps = new EmployeeWrap[obj.emps.length];
for (int i = vars_wraps.length - 1; i >= 0; i--)
vars_wraps[i] = new EmployeeWrap(obj.emps[i]);
Connection _conn = com.acme.resource.ResourceManager.getInstance().getNativeConnection();
oracle.sql.ArrayDescriptor v_ard =
new oracle.sql.ArrayDescriptor("A_EMPLOYEE",
_conn);
oracle.sql.ARRAY v_array =
new
oracle.sql.ARRAY(v_ard, _conn, vars_wraps);
stream.writeArray(v_array);
Dall'analisi
delle precedenti righe si vede come il codice sia
molto simile a quello con cui si scriveva direttamente
un Array nel DB e visto precedentemente: valgono ovviamente
le stesse considerazioni allora esposte. Ovviamente
il codice qui riportato é solo un esempio che
non ha la pretesa d'essere completo: mancano la gestione
degli errori, i controlli per eventuali oggetti uguali
a "null" e così via.
Consiglio di usare le Nested Table di Oracle solo
dopo averci preso confidenza, in quanto un uso errato
dal lato PL-SQL porta ad un degradamento inaccettabile
della performance.
Gli
esempi per gli array e l'oggetto composto sono contenuti
nel file Zip allegato all'articolo. Consiglio inoltre
di leggersi molto attentamente sia la documentazione
di Oracle riguardo l'uso di collections in SQL e PL-SQL,
nonché il paragrafo relativo agli SQL3 Data
Types del Tutorial su JDBC 2.0 di Maydene Fisher disponibile
presso il sito della Java Developer Connection.
Conclusione
Abbiamo
così concluso la descrizione, iniziata nell'articolo
precedente, delle possibilità, offerte dagli
oggetti SQL3 in combinazione con Java e JDBC, che
useremo per il design di un ORM Framework tra Oracle9i
e un'implementazione Java e CORBA(oggetto del prossimo
articolo).
Legenda degli acronimi
Resources
-
JDC on JDBC: http://developer.java.sun.com/developer/Books/JDBCTutorial/index.html
- SQL3 Objects Tutorial: http://java.sun.com/docs/books/tutorial/jdbc/jdbc2dot0/sql3.html
- Oracle Site OTN (Oracle Technology Network) JDBC
Developer's Guide and Reference:
http://download-west.oracle.com/otndoc/oracle9i/901_doc/java.901/a90211.pdf
Esempi
Scarica gli esempi descritti
in questo articolo
|