Java – Tranzakció kezelés és tranzakció izolációs szintek
2010. március 17.
1. Tranzakció kezelés
Akkor lehet rá szükség ha több logikailag öszefüggő SQL utasítást szeretnénk egyszerre (közvetlenül egymás után, egy tranzakcióként) végrehajtani. Ezeknek az SQL utasításoknak általában van egy logikai sorrendje. Ha bármelyik SQL utasítás bukik akkor a logikailag ráépülő többi eljárás is bukik. Például:
- Több oldalas regisztráció, ahol minden oldalhoz tartozó adat külön SQL utasítással bekerül az adatbázisba. Ha az x. oldal után a felhasználó nyom egy cancel-t, akkor az eddig végrehajtott SQL insert-eket is érvénytelenítenünk kell
- Banki tranzakciók – átutalás egy számláról egy másikra, vagy kézpénzfelvét az autómatából: Először megterhelődik az egyik számla a kívánt összeggel, ezután jóváíródik a másik számlán, vagy kiadja az autómata. Ha a második lépés meghiúsul (pl.: Az autómata technikai okok miatt nem tud pénzt kiadni) akkor az első tranzakciót is vissza kell vonni.
Ezekben az esetekben az SQL utasításokat egy tranzakcióban kezeljük. Ez a teljes tranzakció csak akkor hajtódik végre, ha mi erre a programunkból direkt felszólítjuk, egészen addig az adatbázis-kezelő elkülönítve kezeli őket. A tranzakció közben bármikor lehetőség van arra, hogy az addig kiadott SQL utasításokat érvénytelenítsük, ezzel az adatbázis visszaáll az eredeti állapotához.
A tranzakciók kezelésének módját a Connection object-en a setAutoCommit() metódussal tudjuk megváltoztatni:
- connection.setAutoCommit(true): Az új adatbázis kapcsolatok esetén ez a default beállítás. Ha az AutoCommit mód be van kapcsolva akkor minden SQL utasítás különálló tranzakcióként lesz kezelve és végrahajtva.
- connection.setAutoCommit(false): Ebben az esetben (AutoCommit mód kikapcsolva), az SQL utasítások csoportokba rendezhetőek, és csak akkor hajtódnak végre/fejeződnek be, ha a connection-ön meghívjuk a commit() vagy rollback() metódusokat. A commit() hatására a módosítások véglegesen bekerülnek az adatbázisba, míg a rollback() visszaállítja az adatbázist a tranzakció kezdetekori állapotába.
2. Tranzakció izolációs szintek
Azt határozzák meg, hogy hogyan kezelje a szerver az egyidejű hozzáférési kérelmeket (read, update, insert) ugyanahhoz az objektumhoz (tábla, rekord, …).
- TRANSACTION_NONE: A tranzakciók nem támogatottak, nincs tranzakciókezelés.
- TRANSACTION_READ_UNCOMMITED: Olvasáskor mindig az aktuális (módosított) értéket kapjuk, még akkor is, ha az adott insert/update tranzakciót a kezdeményező nem commit-olta. A következő problémák léphetnek fel:
- Dirty reads: A kiolvasott rekord változhat más tranzakciók által a mi tranzakciónk ideje alatt. Tehát előfordulhat, hogy a tranzakciónk elején és végén az adott rekordban más értékek szerepelnek, holott mi nem is változtattuk, csak olvastuk…
- Non-repeatable reads: A tábla egy adott sora törlődhet a mi tranzakciónk közben. Így amikor mi a tranzakciónk elején és végén futtatunk egy-egy select-et, akkor a második esetben hiányozhatnak sorok az eredményből.
- Phantom reads: Hasonló az előzőhöz, csak insert-tel. A táblába új sor (fantom sor) kerülhet egy másik tranzakció által miközben a mi tranzakciónk még fut. Így a tranzakciónk végén több sor lesz az adott táblábam, mint amivel számolhattunk az elején…
- TRANSACTION_READ_COMMITED: Olvasáskor mindig az adott rekord véglegesített értéket kapjuk. Ez az esetek 99%-ra használható, a tranzakcióink mindig csak olyan rekordokat olvasnak, amik kommittálva vannak, azaz nincs nyitott tranzakció, ami dolgozna rajtuk. A baj ezzel az, hogy ha sokan írják és olvassák az adott rekordot vagy táblát akkor könnyen kialakulhat az a helyzet, hogy az olvasó tranzakciók arra várnak hogy az írás (pl egy nagy tábla update-je) befejeződjön. Ez a főleg a rekordokra vonatkozik így csak a dirty read-től véd, probléma lehet:
- a non-repeatable read
- és a phantom read
- TRANSACTION_REPEATABLE_READ: Ez annyival jobb a READ_COMMITTED-nél, hogy már a non-repeatable read hibát is képes kiszűrni a tranzakcióból. Egyszerűbben: csak a recordok commit-olt értékeit használja és a recordok tranzakció közbeni törlése nem befolyásolja a select-eket. Ebben az esetben csak egy probléma marad:
- a phantom read
- TRANSACTION_SERIALIZABLE: Annyival több a REPEATABLE_READ-től, hogy más tranzakció nem írhatja felül a mi tranzakciónk által olvasott értékeket, azaz addig várakoztatja azokat míg be nem fejeződik a tranzakciónk. Így nem fognak tranzakció közben fantom sorok keletkezni a táblában. Itt elég problémás lehet, ha több résztvevő folyamatosan olvas egy táblát, amíg az updatelő szál várakozik, mert a tábla lock-olva van és nem tud bele írni…
Ezek szintek föntről lefelé egyre költségesebbek lesznek az adatbázisnak, így a tranzakciók végrehajtási sebessége lassul. Az izolációs szinteket a Connection objektumon a setTransactionIsolation() metódussal állíthatjuk be a tranzakció megkezdése előtt. Például:
con.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);

2010. március 18. at 13:54
[...] március 18. Az előző poszthoz kerestem dolgokat a neten, és találtam egy nagyon jó kis leírást az optimistic és a [...]