jdbc i integralność danych

0

witam,
mam takie małe pytanie, jak w 'czystym' JDBC zapewnić integralność danych w bazie ( 2 ludzi edytuje jednocześnie tą samą krotkę w bazie, jeden zapisuje zmiany, a ten drugi powinien otrzymać komunikat 'masz nieaktualne dane' czy coś w tym stylu )? W JPA załatwiało to pole z annotacją @Version, natomiast jak by to wyglądało w JDBC? czy są jakieś mechanizmy? czy też trzeba to ręcznie sprawdzać ??

0

IMHO recznie....

pozdrawiam

0

@Shimmi na poziomie bazy rozwiązuje to sama baza (transakcje). Na poziomie JDBC trzeba by było dodać jakiegoś pośrednika, który zarządzał by trwałością encji. W praktyce własna implementacja JDBC. Można też go uprościć. poprzez ręczne flagowanie(wolna, w edycji, do zapisu, odłączona) encji, ale to jest dość trudne i zbyt zagmatwane.

0

może jestem jakis niepełnosprytny ale nie rozumiem...
co znaczy że 'na poziomie bazy rozwiązuje to sama baza' ?? że jak będę miał powiedzmy 2 aplikacje standalone łączące się z tą samą bazą, to ona sama zapewni mi spójność danych ot tak? czy też trzeba cos poustawiać? i czy każda baza?

a co do 'flagowania' to chodzi w sumie o ręczne zajęcie się polem wersji ( sprawdzanie czy aktualne i jeśli tak to zapisujemy zwiększając owe pole o 1 )??

0

@Shimmi, chodzi o integralność na poziomie RDBMS. Czyli przede wszytkim transakcje, blokowanie, sprawdzanie spójności.
Co do flagowania ręcznego.
W obrębie aplikacji wystarczy, że do każdej encji dodasz flagę w postaci timestapa z informacją o ostatniej aktualizacji. Teraz przed aktualizacją wystarczy zadać dodatkowe zapytanie o ta flagę i jezeli jej wartość jest równa tej którą mamy to wszystko jest git. Jeżeli jest większa (późniejsza) to znaczy, że nastapiła już aktualizacja i trzeba pociągnąć nową wersję danych.

0

To z czym masz problem, to zarządzanie współbieżnością. Istnieją 2 podejścia:

  • optymistyczne (czyli zwykle oparte na wersjonowaniu, niekoniecznie widocznym dla programisty, np. MVCC)
  • pesymistyczne (blokady - chyba najpopularniejsze jest dwufazowe tj. 2PL)

Niektóre systemy RDBMS potrafią obsługiwać oba warianty, ale zwykle obsługują jeden. Ostatnio popularniejsze jest MVCC (PostgreSQL, Oracle, Firebird) niż 2PL (IBM DB/2). Oba mają swoje wady i zalety - MVCC jest lepsze jeśli masz dużo "czytaczy" a mało "zapisywaczy".

Generalnie tym POWINNA zajmować się baza danych, nie aplikacja użytkownika. Robienie tego w aplikacji użytkownika wcześniej czy później kończy się albo katastrofą w spójności danych, albo katastrofą w wydajności (słynny przypadek MySQLa i blokowania całych tabel przez LOCK TABLES). Aplikacja powinna natomiast wspomagać RDBMS w przypadku długich transakcji (zwanych czasem sagami). Długie tj. takie, gdzie pomiędzy poszczególnymi operacjami użytkownik np. może zastanawiać się kilka minut. Taka saga składa się z kilku - kilkunastu osobnych transakcji, które tworzą jakąś całość. Wtedy framework O/R, jak Hibernate i wersjonowanie jest przydatne.

Dlatego w JDBC nie musisz robić prawie nic, prócz wyłączenia auto commit. No i musisz oznaczać końce transakcji przez ręczne wywołanie commit, bo inaczej dane w bazie Ci się nie zapiszą.

Musisz mieć też odpowiedni RDBMS i odpowiednio go skonfigurować. Dobre jest własciwie wszystko poza MySQL, SQLite i HSQLDB. Jeśli użyjesz np. Oracle, ASA, PostgreSQL przy ustawieniu izolacji transakcji na SERIALIZABLE, a chyba tylko taki wchodzi w grę, jeśli chcesz mieć absolutną spójność, próba zapisu tego samego rekordu równocześnie skonczy się zwinięciem jednej z transakcji. Innymi słowy jeden klient dostanie wyjątek. Wszystkie jego wcześniejsze zapisy zostaną anulowane. Ty w aplikacji powinieneś go wyłapywać gdzieś na b. wysokim poziomie i ponowić całą transakcję, tak żeby użytkownik o niczym nie wiedział.

0

dz. dokł. o to chodziło :)

p.s.

a jak by się do tego miało 'Select ***** for update' ?? no bo niby zakłada on na wiersz LOCKa aż do wykonania commita...ale jakoś nie chce mi to wyjść i żaden wyjątek nie chce się wywalić, czy mógłbym ewentualnie o jakieś kilka linijek przykładowego kodu prosić??

0

Dodam tylko, że można zmieniać poziomy izolacji dla jdbc poprzez metodę Connetion: setTransactionIsolation(int level).

Można zrobić tak:
-gdy odczytujemy coś z bazy, to ustawiamy na TRANSACTION_READ_COMMITTED
-gdy zapisujemy, ustawiamy na TRANSACTION_REPEATABLE_READ, sprawdzamy czy w bazie jest stan, który był wcześniej(musimy go gdzieś wcześniej zapamiętać). Jeżeli się zgadza, to go zmieniamy, a jak nie to informujemy użytkownika.

http://java.sun.com/javase/6/docs/api/java/sql/Connection.html

P.S. To co tu piszę może nie być prawdą, tak wnioskuję tylko z dokumentacji. Sam używam JPA i BMT lub CMT.

0

@Shimmi: SELECT... FOR UPDATE różni się tylko tym, że od razu zakłada blokadę zapisu na rekord. Jeśli masz izolację transakcji ustawioną na SERIALIZABLE, to wtedy takie wywołanie dla już zablokowanego rekordu spowoduje oczekiwanie na zakończenie blokującej (tej drugiej) transakcji. Jeżeli blokująca transakcja zakończy się powodzeniem, transakcja oczekująca zostanie przerwana i anulowana. Nie można bowiem dopuścić, aby transakcja czytająca starsze dane mogła nadpisywać nowsze. Jeśli blokująca transakcja zostanie anulowana z jakiegokolwiek powodu, np. wyśle ROLLBACK, transakcja oczekująca przestanie oczekiwać i będzie kontynuowana.

To co napisałem w powyższym akapicie oraz w poprzednim poście to prawda tylko dla MVCC i SERIALIZABLE.
Jeśli masz np. 2PL / SERIALIZABLE to wywołanie SELECT FOR UPDATE spowoduje jedynie czekanie na zakończenie drugiej transakcji i kontynuację pierwszej niezależnie od powodzenia drugiej. Zwinięcie może nastapić tylko w przypadku deadlocku (transakcja A czeka na B, B czeka na A), który w 2PL jest bardziej prawdopodobny niż w MVCC.

W MVCC / READ_COMMITTED chyba jest tak samo, ale nie dam głowy - serwery róznie mogą to traktować. Ten tryb i tak nie zapewnia spójności danych, więc dla "ułatwienia" mogą nie zwijać transakcji.

@_krzysiek85: Jeśli transakcje tylko__czytające są READ_COMMITTED, to mogą czytać niespójne dane.
Jesli dużo czytasz, a malo piszesz - to niewielkie ryzyko niespójności być może jest zrekompensowane wiekszą wydajnością - READ_COMMITTED jest mniej zasobożerne niż REPEATABLE_READ, ale musisz to i tak zweryfikować dla Tweojego RDBMSa. Niektórym RDBMSom jest wszystko jedno (np. PostgreSQL). No i kwestia zastosowań aplikacji. W aplikacjach finansowych to chyba niezbyt dobry pomysł. Jeszcze ktoś zobaczy, że mu odjęło z jednego konta, a nie dodało do drugiego i zanim odświeży stronę, dostanie zawału.

Jeśli transakcje zapisujące są w REAPEATABLE_READ, to przynajmniej do bazy jakiś dużych niespójności nie powrzucasz, ale uwaga REAPEATABLE_READ też nie gwarantuje absolutnej spójności, bo możesz mieć tzw. "phantom reads", czyli możesz czytać nowe krotki, których wcześniej w tej samej transakcji nie było. Ja tam jestem zwolennikiem SERIALIZABLE wszędzie i dobrze dostrojonych zapytań / indeksów. Wtedy jest naprawdę ACID, a nie jakieś półśrodki.

1 użytkowników online, w tym zalogowanych: 0, gości: 1