Newsgroup: it.comp.java
Titolo: lock su applicativo

Ho scritto un'applicazione java che si scarica dei dati da un database e dà
la possibilità di apportare modifiche ad una delle entry.
Il problema è che tale applicativo viene utilizzato da PIU' persone che
lavorano sugli STESSI dati, e potrebbe capitare una situazione di
inattendibilità dei dati. Del tipo che ciò che è stato scaricato dal db non
è più aggiornato in quanto modifcato da qualcun altro poco dopo la lettura
dal db.
Secondo voi qual'è la maniera migliore per evitare problemi derivanti da una
simile configurazione? Perlomeno vorrei ottenere (in una maniera elegante ed
efficiente) che non vengano modificati dati non aggiornati (restituendo
magari un messaggio di errore).
Che tipo di lock potrei utilizzare? I DataSet possono essermi utili in
questo? Se si, sono implementati in qualche package?
Grazie in anticipo per le risposte.
fab

il lock lo devi fare dell'intera applicazione o dei singoli record
utilizzati?

 aggiornati (restituendo magari un messaggio di errore).
La tecnica per eccellenza č la seguente: quando recuperi i dati da 
modificare, ti salvi in un buffer i valori delle colonne. Al momento in cui 
costruisci lo statement dell'update, aggiungi fra le restrizioni di where 
le colonne che l'utente ha modificato per il loro valore originale. In 
questo modo l'update ha effetto solo sulle righe le cui colonne in 
aggiornamento non sono variate da quando l'hai lette. Non solo, ma se 
updateCount() dello statement non ti restituisce il valore atteso puoi 
gestire elegantemente la situazione.


sul record

 gestire elegantemente la situazione.
In realtà il problema è differente. Allora, immagina uno scenario dove
l'utente A carica dal database una serie di record, ognuno dei quali è
caratterizzaro da uno stato. In particolare intende modificare lo stato del
record x. Allo stesso modo un secondo utente B esegue la stessa operazione
di visualizzazione ma, prima dell'utente A, modifica lo stato del record x.
A questo punto lo scenario è il seguente: A non ha più la situazione
aggiornata del database, perchè il record che intendeva modificare è già
stato modificato dall'utente B che ha agito più tempestivamente di lui.
Adesso quello che mi chiedo è questo: c'è una maniera più elegante di
verificare l'attendibilità dei dati con un'ulteriore accesso al database
prima dell'update (prima cosa che viene in mente di fare) ?
C'è anche da dire che sono diversi i valori (campi della riga) per i quali
si possa effettivamente dire che lo stato del recordo è cambiato. Io avevo
pensato di ottenere una sorta di valore di checksum di una riga, magari
combinando fra loro i campi ottenendone un determinato ed univoco valore
hash. Ma purtroppo ciò non toglie che dovrò sempre controllare che un dato
non sia stato modificato prima di eseguire l'update, quindi dovrò accedere
di nuovo al database prima di eseguire la transazione. Credo che un utilizzo
opportuno dei dataset (c'è una classe apposita sviluppata dalla borland)
implichi un meccanismo automatico di verifica prima dell'update.
fab

 In realtà il problema è differente. 
[cut]
Io lavoro anche con altri strumenti prettamente mirati a scrivere 
applicazioni client server, uno di questi è PowerBuilder. La soluzione 
che lui attua (in automatico, tramite la datawindow) è quella che ti ho 
descritto io. Quando popola il suo resultset (datawindow o datastore) 
mantiene due buffer di dati, uno per i valori correnti e uno per i valori 
originali, l'utente modifica ovviamente i valori correnti. Quando procede 
all'update, ricostruisce uno statement per ciascuna riga modificata e in 
questo statement ricostruisce una condizione di where per aggiornare la 
riga modificata sul database. In fase di design si possono scegliere tre 
metodi che lui usa per costruire la condizione di where:
- Solo la chiave primaria della riga
- Chiave primaria più le colonne modificate
- Chiave primaria più le colonne modificabili
Nel primo caso, come potrai immaginare effettua l'aggiornamento 
incondizionato sulla riga e fallisce solo se la riga è stata cancellata 
dal database.
Nel secondo caso aggiunge alla where, oltre alla chiave, la condizione 
che le colonne modificate siano uguali al valore originale 
precedentemente salvato nel buffer. Questo ti preserva di sovrascrivere 
modifiche fatte da altri ma ti consente di aggiornare righe di cui sono 
state modificate delle colonne che tu non stai aggiornando.
Nel terzo caso aggiunge (oltre sempre alla chiave) la condizione che 
*tutte* le colonne aggiornabili della tabella siano uguali al loro valore 
originale. Questo impedisce di aggiornare righe che sono state modificate 
in una qualsiasi colonna.
In ogni caso, quando uno di questi update fallisce (non aggiorna nessuna 
riga) restituisce un errore (guardacaso, "row changed between retrieve 
and update") e a quel punto il programmatore gestisce la situazione.
Probabilmente delle classi come quella Bolrand implementano un meccanismo 
del genere, perchè è quello più sicuro in assoluto nonchè database 
independent. Se infatti tu ipotizzassi di fare un controllo con una 
select prima dell'update avresti questi problemi:
- l'overhead di un accesso ulteriore al database
- rispetto a ciò che ti ho spiegato è ridondante
- dovresti applicare un lock sulla riga durante il controllo perchè, per 
quanto veloci, controllo e update non sono atomici. Inoltre con una 
semplice select tu leggi l'ultima situazione consolidata, se c'è una 
transazione in corso (magari anche lunga) su quella riga la select ti 
restituirà i dati *prima* della transazione mentre l'update attenderà la 
fine della transazione. Ovvero sarebbe un controllo totalmente 
inaffidable. Dovresti fare una "select for update" (nella sintassi di 
oracle) ma non tutti i db supportano questa opzione, anzi.