2009-09-22 5 views
5

Sto lavorando a un'app Web collegata a Oracle. Abbiamo una tabella in oracolo con una colonna "attivata". Solo una riga può avere questa colonna impostata su 1 alla volta. Per far rispettare ciò, abbiamo utilizzato il livello di isolamento SERIALIZZATO in Java, tuttavia stiamo eseguendo l'errore "non è possibile serializzare la transazione" e non è possibile capire perché.READ livello di isolamento del database COMMESSO in oracle

Ci chiedevamo se un livello di isolamento di READ COMMITTED avrebbe svolto il lavoro. Quindi la mia domanda è questa:

Se abbiamo una transazione che coinvolge il seguente SQL:

SELECT * 
FROM MODEL; 

UPDATE MODEL 
SET ACTIVATED = 0; 

UPDATE MODEL 
SET ACTIVATED = 1 
WHERE RISK_MODEL_ID = ?; 

COMMIT; 

Dato che è possibile che più di una di queste operazioni da eseguire, allo stesso tempo, è vero essere possibile per più di una riga MODELLO per avere il flag attivato su 1?

Qualsiasi aiuto sarebbe apprezzato.

risposta

3

la soluzione dovrebbe funzionare: il primo aggiornamento bloccherà l'intera tabella. Se un'altra transazione non è terminata, l'aggiornamento attenderà. Il tuo secondo aggiornamento garantirà che solo una riga avrà il valore 1 perché stai bloccando la tabella (non impedisce tuttavia le istruzioni INSERT).

È inoltre necessario assicurarsi che la riga con lo RISK_MODEL_ID esista (o si avrà zero riga con il valore '1' alla fine della transazione).

Per evitare istruzioni INSERT simultanee, la tabella (in modalità ESCLUSIVA) è LOCK.

+0

Simile, ma mi piacerebbe BLOCCO MODELLO in modalità esclusiva e fare un UPDATE MODELLO SET attivato = 0 WHERE attivato = 1 Meno file vengono aggiornati, quindi è un po 'più performante, e la tabella BLOCCO impedirebbe l'attività simultanee sul tavolo. –

3

Si potrebbe utilizzare un unico, indice basato funzione di lasciar Oracle gestire il vincolo di avere solo una sola riga con bandiera attivato impostato a 1.

CREATE UNIQUE INDEX MODEL_IX ON MODEL (DECODE(ACTIVATED, 1, 1, NULL)); 

Ciò fermare più riga avente la bandiera impostato su 1, ma non significa che c'è sempre una riga con il flag impostato su 1.

+0

+1: semplice, efficiente, multi-user safe –

2

Se ciò che si desidera è assicurarsi che sia possibile eseguire una sola transazione alla volta, è possibile utilizzare la sintassi FOR UPDATE. Poiché hai una singola riga che richiede il blocco, questo è un approccio molto efficiente.

declare 
    cursor c is 
     select activated 
     from model 
     where activated = 1 
     for update of activated; 
    r c%rowtype; 
begin 
    open c; 
    -- this statement will fail if another transaction is running 
    fetch c in r; 
    .... 
    update model 
    set activated = 0 
    where current of c; 

    update model 
    set activated = 1 
    where risk_model_id = ?; 

    close c; 

    commit; 
end; 
/

Il commit libera il blocco.

Il comportamento predefinito è attendere che la riga venga liberata. Altrimenti possiamo specificare NOWAIT, nel qual caso qualsiasi altra sessione che tenti di aggiornare la riga attiva corrente fallirà immediatamente, oppure potremo aggiungere un'opzione WAIT con un tempo di polling. NOWAIT è la possibilità di scegliere di evitare assolutamente il rischio di appendere, e ci dà anche la possibilità di informare l'utente che qualcun altro sta aggiornando la tabella, che potrebbero voler sapere.

Questo approccio è molto più scalabile rispetto all'aggiornamento di tutte le righe nella tabella. Utilizzare un indice basato sulle funzioni come mostrato da WW per applicare la regola che solo una riga può avere ACTIVATED = 1.

+0

+1: bella soluzione –