Quando si lavora in concorrenza sugli stessi dati, cosa alquanto probabile con una applicazione web, è d'obbligo stabilire una politica di blocco dei dati.
Esistono due approcci speculari al lock dei dati:
- ottimistico, ovvero si presuppone che nella maggior parte dei casi non si verifichino collisioni quindi si consente a tutti di lavorare e si segnala l'errore solo quando si tenta di aggiornare dati resi obsoleti da una modifica antecedente;
- pessimistico, dove si presuppone una forte incidenza di collisioni in modifica sui dati e quindi si provvede a rendere inaccessibile il dato anche in pre-modifica fino a che il dato non è stato aggiornato.
Il framework fornisce già il supporto al lock ottimistico ereditandolo da Hibernate: su ciascun BusinessObject è stata definita una proprietà version da utilizzare a questo scopo.
Facciamo un esempio in cui due client, A e B, desiderano entrambi modificare le informazioni salvate sul database relativamente ad uno stesso oggetto O con id 10. Ipotizziamo questa sequenza di eventi:
- A richiede i dati di O
- B richiede i dati di O
- A richiede il salvataggio di O
- B richiede il salvataggio di O
E' evidente che al punto 4 si verifica una condizione di collisione individuabile dal lock ottimistico altrimenti il salvataggio operato da A sarebbe andato perso inevitabilmente! Ora ripetiamo la stessa sequenza di operazioni considerando il numero di _versione_ dell'oggetto O:
- A richiede i dati di O (la versione di O è 1)
- B richiede i dati di O (la versione di O è 1)
- A richiede il salvataggio di O
- La versione di O a disposizione di A viene confrontata con quella presente sul database
- I dati vengono salvati e la versione di O incrementata a 2
- B richiede il salvataggio di O
- La versione di O a disposizione di B viene confrontata con quella presente sul databas
- Le versioni non coincidono e viene segnalato un conflitto all'utente B mediante una LockException
Per ottenere il supporto a questa gestione del lock, che ritengo la più appropriata nella maggior parte dei casi, è sufficiente definire un mappaggio apposito nei file .hbm, secondo quanto già riportato nella documentazione di Hibernate. Se si utilizza XDoclet per la generazione dei file .hbm, operazione fortemente consigliata perchè consente di tenere tutte le informazioni in un unico file, allora si dovrà provvedere a ridefinire il metodo version in questo modo:
/**
* @hibernate.version
*/
public void getVersion() { return super.getVersion(); } La semplice dichiarazione riportata sopra risolve il problema in via definitiva e non introduce rallentamenti in quanto qualunque compilatore Java accettabile è in grado di ottimizzare il metodo (l'operazione si chiama
method inlining).
Ricordo che una volta introdotta questa nuova proprietà è
necessario inserire nei moduli di modifica dei
BusinessObject versionati un nuovo campo nascosto
version attraverso il quale trasportare la nuova informazione.
Mi ripeto: questa operazione, cioè inserire il campo nascosto nel form, diventa
necessaria altrimenti non riuscirete più ad aggiornare un oggetto dopo averlo creato!