Newsgroup: it.comp.java
Titolo: accesso ad un database Oracle

Se qualcuno si e' gia' imbattuto nel mio problema, forse puo' darmi una 
dritta.
Vi spiego.
Devo accedere ad un database Oracle con java.
Con altri linguaggi utilizzo un oggetto ADO, al quale passo, tra gli 
altri parametri, il nome dell'etichetta di connessione al DB, i cui 
parametri sono definiti nel file di Oracle TNSNAMES.ORA.
Pensavo che con java ci fosse qualcosa di simile, ma il jdbc si comporta 
con una leggera differenza: invece dell'etichetta del DB con la quale 
accedere al file TNSNAMES.ORA vuole proprio la stringa di descrizione 
della connessione, la medesima che e' presente nel TNSNAMES.ORA
Per ragioni di economicita' di gestione, non posso permettere che 
qualora vi siano dei cambiamenti ai parametri del database, oltre al 
TNSNAMES.ORA vengano modificati tutti i files di configurazione di tutte 
le applicazioni che vi accedono.
Quindi pensavo di creare un meccanismo che consenta all'applicazione di 
ricavare la stringa di descrizione dal file TNSNAMES.ORA medesimo.
Se non che, mi sono imbattuto in un paio di difficolta' di ordine pratico.
Questa roba deve girare in ambiente windows.
La dichiarazione della variabile ORACLE_HOME, che sotto unix sta in 
environment, sotto windows sta nel registro, e java, essendo platform 
independent, al registro non ci accede, se non con metodi sporchi che 
intendo evitare.
Potrei passargli a mano un -DORACLE_HOME=C:\oracle\ora92, ma quando un 
dopodomani aggiorneranno il DB ad un'altra versione di oracle, cambiera' 
il percorso e l'applicazione non girera' piu'.
C'e' un modo per gestire il tutto in modo dinamico, in modo che 
qualunque cosa succeda al DB, l'applicazione non richieda di dovergli 
specificare nuovamente dove si trova Oracle?

 il percorso e l'applicazione non girera' piu'.
A me non sembra che la tua sia una soluzione poco manutenibile. Scusami 
tanto, ma una migrazione
di versione non è una di quelle cose che si fa senza pensarci almeno un 
secondo..penso che farete
due prove prima di piallare la vecchia versione, no ?
Ciao,
    Scorpio.

 specificare nuovamente dove si trova Oracle?
Il driver JDBC di Oracle permette due tipologie di accesso al database:
1) Modalita` Thin, ovvero specifichi nella stringa di connessione l'host su
cui si trova il database, la porta su cui e` in ascolto (di solito 1521) e
il sid del database.
2) Modalita` OCI, ovvero specifichi nella stringa di connessione il nome
definito nel file TNSNAMES.ORA
La scelta di uno o dell'altro metodo dipende molto da cosa hai intenzione di
fare. Il primo metodo ti assicura una portabilita` dell'applicazione
indipendentemente dal fatto che vi sia installato o meno un client Oracle
sul pc su cui andra` a girare. Il secondo metodo, invece, richiede
espressamente l'installazione di un client Oracle sulla macchina su cui
l'applicativo andra` a girare, ma in compenso risulta molto comodo nel
momento in cui sai per certo che l'architettura e` stabile (e.g. stai
sviluppando un applicativo verticale per un'azienda in cui in tutti i pc e`
installato un client Oracle). Personalmente ti consiglio di tornare
indietro sui tuoi passi e di utilizzare il primo metodo a prescindere
Enrico

Scorpio ha scritto:

Si tratta di un database aziendale con centinaia di applicazioni e 
migliaia di tabelle, e le persone che ne seguono la manutenzione non 
sanno e non vogliono sapere niente delle applicazioni che vi accedono.
Le eventuali due prove che faranno in caso di upgrade saranno solo con 
le applicazioni principali e vitali, non certo con un batch notturno che 
estrae dei dati da una tabella per metterli in un file.csv.
E io che sto sviluppando una applicazione che vi accede non voglio fare 
una cosa che richieda troppi smanacciamenti per la messa in produzione.
Piu' riesco a scrivere una procedura autosufficiente e meglio e', sia 
per me, sia per chi deve gestirla, sia per chi dovra' occuparsene dopo 
di me.
Sono qui a chiedere apposta se qualcuno per caso ci e' gia' passato e ha 
risolto in qualche modo.

 indietro sui tuoi passi e di utilizzare il primo metodo a prescindere
Considerando che la procedura dovra' girare solo su una macchina, e su 
quella macchina il client oracle esiste sicuramente, mi stai dicendo che 
posso usare il metodo oci e indicare solo l'etichetta del TNSNAMES.ORA
Questo risolverebbe il mio problema, ma ho provato in tutti i modi con 
l'oci e va sempre in eccezione.
Altre applicazioni sviluppate da miei colleghi, da cui ho preso spunto, 
si sono sempre avvalse del thin e gli passano tutta la descrizione, ed 
e' quello che sto facendo pure io, ma questa cosa di dover passare una 
descrizione, che e' presente anche altrove, mi scoccia un po'.
Purtroppo il tempo per fare delle prove per migliare questi aspetti di 
manutenzione non lo concedono mai: ai dirigenti basta che la procedura 
funzioni, e chissenefrega se ci sono dei dati hard-coded, e la 
documentazione non c'e' mai il tempo per sciverla o se la scrivi la perdono.
Poi quando capitano i casini c'e' da perderci giorni per mettere a posto 
una stupidaggine.

 descrizione, che e' presente anche altrove, mi scoccia un po'.
Direi perche` stai cercando il pelo nell'uovo :)
Alla fine, la modalita` thin funziona, e considerando che l'unica cosa che
cambia rispetto alla modalita` oci e` di specificare (oltre al nome del
database) l'host e la porta su cui si trova in ascolto il database, direi
che e` accettabile come compromesso. Infine, per il problema da te
specificato nell'altro post, evita di mettere i parametri di connessione
nel codice, ma mettili in un file properties utilizzato dall'applicazione.
Al cambio di qualche parametro bastera` che fermi l'applicazione, modifichi
il file, e riavvii l'applicazione
Enrico
P.S. per approfondire il discorso, guarda questo:
http://www.oracle.com/technology/tech/java/sqlj_jdbc/htdocs/jdbc_faq.htm

 
http://www.oracle.com/technology/tech/java/sqlj_jdbc/htdocs/jdbc_faq.htm
Questo me lo leggo con calma lunedi in ufficio: ho bisogno di 
approfondire meglio questi aspetti, perche' se questa procedura che sto 
scrivendo riesce a soddisfarmi, usero' java piu' spesso in futuro.

 che mi serve (sempre che non ne trovo uno gia' pronto) e avro' risolto.
Se devi arrivare a questo, allora direi che ti sei complicato ancora di piu`
la vita piuttosto che semplificartela
Enrico

Enrico 'Henryx' Bianchi ha scritto:

Evidentemente abbiamo dei concetti di complicazione di vita diversi, io 
e te: per me, piu' cose riesco a far fare al codice, meno dovro' farne 
io in seguito quando questo verra' messo in produzione (e quindi fuori 
dal mio controllo).
Capisco benissimo che e' molto piu' facile fare un copia/incolla della 
definizione che c'e' nel TNSNAMES.ORA e passarla con un -D al programma 
java, poi pero' il giorno che la modificheranno (e non me lo vengono 
certo a dire che l'anno modificata) il programma non funzionera' piu' 
correttamente e io ci dovro' perdere come minimo un paio di giorni non 
pagati per capire perche', quindi preferisco perderci un giorno in piu', 
pagato, oggi.
Senza contare che il parserino, una volta scritto, lo posso riutilizzare 
per tutte le applicazioni di accesso ai db che mi faranno scrivere 
successivamente, che andranno autonomamente a prendersi le definizioni 
di accesso.
Io lavoro in un ambiente in cui le competenze (e permessi) di sistemisti 
e amministratori sono ben circoscritte: chi si occupa dei DB, si occupa 
solo dei DB; chi si occupa delle reti, si occupa solo delle reti, etc...
L'unica costante di tutto l'insieme e' che se qualcosa non funziona, la 
colpa e' sempre del programmatore, il quale deve perdere tempo a 
dimostrare che invece il problema sta altrove, ricercare comunque la 
soluzione e proporla a chi ha la competenza di metterla in pratica.
Pertanto sotto questo punto di vista, per me il non complicarmi la vita 
prevede il mettermi al riparo da possibili rogne future.
Magari chi opera in altre realta' ha maggiori poteri, maggiore 
visibilita' del tutto e puo' permettersi di allegerire il codice e 
complicare l'aspetto sistemistico e la configurazione.

UCAS, boh!!!
concordo sul fatto che puoi facilmente parametrizzare la tua
applicazione usando un file di properties dove definire tutta la tua
configurazione per il db, più dinamico di così oppure non capisco io
e me ne dispiace.

 dal mio controllo).
Ma perché allora non cerchi di capire perché non ti funziona il driver OCI?
Certo che se riesco a far fare al codice una cosa che altrimenti dovrei 
fare io è meglio. Ma meglio ancora se riesco a farlo fare a del codice 
già scritto e testato da altri (il driver oracle in questo caso).
Fare un parser per il file TNSNAMES.ORA (che già di suo mi è stato 
sempre antipatico come formato) sembra anche a me una complicazione inutile.
Andrea

 Andrea
-- 
questo articolo e` stato inviato via web dal servizio gratuito 
http://www.newsland.it/news
 segnala gli abusi ad 
ab...@newsland.it

Ecco qua.
Alla fine ho risolto a modo mio.
Ho scritto una classe per leggere il TNSNAMES.ORA con un metodo che 
restituisce la stringa di descrizione.
E ho predisposto il batch di lancio dell'applicazione java per definire 
ORACLE_HOME e passarlo all'applicazione stessa.
L'unica cosa che non mi soddisfa e' che ho dovuto scrivere
	String s = "";
per inizializzare una stringa vuota per poi appenderci i caratteri che 
leggo dal file.
Forse si puo' fare un po' meglio.
Se qualcuno fosse interessato lascio qui di seguito i files.
[LANCIA.BAT]
SET REGKEY=HKLM\Software\Oracle
SET REGVAL=ORACLE_HOME
FOR /F "usebackq skip=4 tokens=1,3" %%i IN (`reg query %REGKEY% /v 
%REGVAL%`) DO SET ORACLE_HOME=%%j
java -DORACLE_HOME=%ORACLE_HOME myJavaApp
[TNSnames.java]
import java.text.*;
import java.util.*;
import java.io.*;
class TNSnames
{
/*
     class TNSnames
     Autore  : Roberto Montaruli
     Versione: 1.0 - 26/11/2007
     Gestisce il parsing del file TNSNAMES.ORA
     Metodi:
       TNSnames(nomefile)    - Apre il file TNSNAMES.ORA (indicato in 
nomefile) e lo carica in una Hashtable
       readFile()            - legge il file e lo carica in una Hashtable
       getDescription(label) - ritorna la descrizione del database 
corrispondente alla label
*/
   private String filename;  // pathname del TNSNAMES.ORA
   private FileReader fr;
   private Hashtable TNS = new Hashtable();  // Qui finiscono le righe 
del TNSNAMES.ORA
   // =============================================
   // Costruttore
   public TNSnames(String fname) throws IOException
   {
     filename = fname;
     readFile();
   } // TNSNames
   public boolean readFile() throws IOException
   {
     int brackets = 0;
     boolean comment = false;
     String s = "";
     fr = new FileReader(filename);
     int i;
     // Fino alla fine
     while ((i = fr.read()) != -1)
     {
       char c = (char) i;
       // Il # introduce righe di commento che viene ignorato
       if (c == '#')
       {
         comment = true;
         continue;
       }
       // Il fine linea interrompe l'eventuale commento e comunque 
vengono ignorati
       if ((c == '\r') || (c == '\n'))
       {
         comment = false;
         continue;
       }
       // Gli spazi e i tabs vengono ignorati
       if ((c == '\t') || (c == ' '))
       {
         continue;
       }
       // Se non e' un commento aggiunge il carattere
       if (!comment)
       {
         s += c;
         //list[count] += c;
         // Se e' una parentesi aperta incrementa il contatore
         if (c == '(')
         brackets++;
         // Se e' una parentesi chiusa decrementa il contatore e, se 
zero, il record e' finito.
         if (c == ')')
         {
           brackets--;
           if (brackets == 0)
           {
             i = s.indexOf('=');
             TNS.put(s.substring(0, i), s.substring(i + 1));
             s = "";
           }
         }
       }
     }
     fr.close();
     return true;
   }
   public String getDescription(String DBlabel)
   {
     return (String)TNS.get(DBlabel);
   } // getDescription
} // TNSNames

 Forse si puo' fare un po' meglio.
Si puo' fare molto di meglio, tanto per cominciare non facendo per 
niente questa cosa, ma il consiglio non ti e' piaciuto.
Si può migliorare evitando di utilizzare un FileReader (non bufferizzato 
se non sbaglio) leggendo un carattere alla volta. Tanto per fare un 
esempio invece di
	fr = new FileReader(filename);
si potrebbe utilizzare
	fr = new BufferedReader(new FileReader(filename));
Va assolutamente migliorato nel fatto che non gestisci correttamente la 
chiusura del file (se per qualche motivo hai un'eccezione il file rimane 
aperto).
Andrebbe migliorato lo stile di programmazione nell'utilizzo dei campi 
anziché di una semplice variabile locale per un oggetto che viene 
utilizzato esclusivamente all'interno di un metodo.
Poi si puo' fare di meglio utilizzando un'espressione regolare per 
rendere meno complesso il parsing del file.
Si potrebbe almeno utilizzare uno string builder anziché la 
concatenazione di stringhe.
Infine ripeto il mio consiglio: perché non scopri semplicemente perché 
non ti funziona il driver OCI?
Andrea


 non ti funziona il driver OCI?
Perche' non ho il tempo per cercare una soluzione che non so neanche se 
funziona.
Intanto adesso ho messo il programma in produzione e sta girando.
Se ho tempo provo ad indagare circa il driver OCI, ma anche altre 
applicazioni fatte da colleghi, che utilizzano il jdbc, si collegano col 
thin.

 thin.
Il driver OCI è meno comune dato che richiede l'installazione del driver 
oracle sulla macchina, ma ti assicuro che funziona (lo usiamo su alcuni 
nostri clienti dove c'è una particolare configurazione di oracle che il 
driver thin non riesce a gestire).
Andrea