java

Transakcje w J2EE: Java Transaction API i Java Transaction Service

Spis treści

1 Co zawiera tekst ?
2 Transakcje
     2.1 Czym jest transakcja ?
     2.2 Właściwości transakcji
3 Transakcje w środowisku J2EE
     3.1 Protokół zatwierdzania dwufazowego 2PC ( two Phase Commit )
4 Transakcje zarządzane przez kontener ( Container-managed )
     4.1 Atrybuty transakcji zarządzanych przez kontener
     4.2 Pozostałe cechy obsługi przez kontener
     4.3 Uwagi
     4.4 Zalety i wady automatycznego zarządzania transakcjami
5 Transakcje zarządzane na poziomie ziarna ( Bean-managed ) - JTA
     5.1 Wersja
     5.2 Jak używać JTA ?
     5.3 Interfejs UserTransaction
     5.4 Inne interfejsy
     5.5 Poziomy Izolacji
     5.6 Uwagi
     5.7 Zalety i wady ręcznego zarządzania transakcjami
6 Testy porównawcze
7 Java Transaction Service
     7.1 Wersja
     7.2 Czym jest JTS ?
     7.3 Jak działa JTS ?
     7.4 Cechy JTS
8 Łącza do innych stron



1 Co zawiera tekst ?

W poniższym tekście zostanie omówiony sposób używania przez programistę transakcji w środowisku J2EE. Zaprezenowane zostaną różne rodzaje przetwarzania transakcji dostępne na platformie J2EE - zarządzanie transakcjami w sposób automatyczny, na poziomie kontenera EJB, dokonywane przez serwer aplikacyjny oraz zarządzanie "ręczne" na poziomie ziaren EJB lub ich metod, poprzez Java Transaction API. Zajmiemy się też, choć skrótowo, bo jest on nieistotny dla przeciętnego programisty, interfejsem Java Transaction Services, który jest w pewnym uproszczeniu niskopoziomową biblioteką Javową do obsługi transakcji. Do zrozumienia poruszanych problemów niezbędna okaże się wiedza z przedmiotu "Programowanie Współbieżne", a konkretnie jej część dotycząca transakcji oraz ogólna znajomość środowiska J2EE.




2 Transakcje



2.1 Czym jest transakcja ?

Transakcja to zestaw operacji zmieniających jakieś dane, tak aby z zewn+/-trz było to widziane jako pojedyńcza operacja.

2.2 Właściwości transakcji

Transakcje są:

atomowe - co oznacza że albo wykonują się wszystkie operacje z transakcji albo żadna z nich.

zgodne - to znaczy po ich wykonaniu system nie może się rozpójnić

izolowane - wykonanie jednoczesne kilku transakcji daje takie same efekty jak wykonanie ich po kolei

trwałe - efekty wykonia transakcji są trwałe.




3 Transakcje w środowisku J2EE

Transakcje w J2EE składają się z wywołania metody lub kilku metod ziaren EJB i operacji na EIS (Enterprise Information System - baza danych). W obecnej implementacji SUNowskiego mechanizmu zarządzającego transakcjami są one "płaskie", natomiast transakcje zagnieżdżone są niezaimplementowane. Oznacza to, że w każdej chwili wątek może uczestniczyć w maksimum jednej transakcji. Specyfikacja J2EE wymaga obsługi transakcji płaskich, a nie wymaga ( ale też nie zabrania ) obsługi wersji zagnieżdżonej. Próba wywołania kolejnej transakcji, gdy jakaś już się odbywa, a implementacja JTA nie obsługuje transakcji zagnieżdżonych spowoduje podniesienie wyjątku.

Transakcje w J2EE są rozproszone - mogą dotyczyć kilku baz danych ( EIS ) i może w nich uczestniczyć wiele programów klienckich i ziaren EJB jednocześnie. Programista ma zapewnioną pełną przezroczystość lokalizacji bytów uczestniczących w transakcji, a to dzięki protokołowi 2PC



3.1 Protokół zatwierdzania dwufazowego 2PC ( two Phase Commit )

W J2EE zastosowano model dwufazowego zatwierdzania transakcji. W pierwszej fazie główny koordynator transakcji wysyła do wszystkich bytów uczestniczących w transakcji ( transakcje wykonują się w środowisku rozproszonym ) prośbę o zgodę na commit. Jeśli żaden z nich nie zaprotestuje to następuje zatwierdzenie transakcji i wszyscy są szczęśliwi. Jeśli natomiast któryś z koordynatorów nie odpowie na prośbę o zatwierdzenie wysłaną w pierwszej fazie lub odpowie negatywnie to wtedy transakcja jest anulowana a koordynator transakcji informuje wszystkie byty w niej uczestniczące o niepowodzeniu. Dzięki protokołowi 2PC możliwa jest koordynacja rozproszonych transakcji, np. w sytuacji gdy w jednej transakcji zmieniamy dane ulokowane w 2 różnych bazach danych ( 2 różnych EIS).




4 Transakcje zarządzane przez kontener ( Container-managed )

Sterowanie transakcjami zarządzanymi przez kontener odbywa się poprzez ustawienie atrybutu trans-attribute w deskryptorze aplikacji. Kontener sam korzysta z JTA, zwalniając programistę z konieczności pisania kodu obsługującego transakcję.

Jest to więc sterowanie deklaratywne, nie wymagające od programisty samodzielnej obsługi transakcji. Atrybut musi być ustawiony dla każdej metody interfejsu zdalnego w ziarnach sesyjnych i każdej metody interfejsu zdalnego i domowego w ziarnie encyjnym dla której chcemy zdefiniować sposób obsługi transakcyjności.



4.1 Atrybuty transakcji zarządzanych przez kontener

Atrybut określa spsosób przetwarzania transakcji przez kontener i może przyjąć jedną z wartości:

Required - Kontener upewnia się, że metoda EJB zawsze będzie wołana w transakcji JTA. Jeśli klient nie jest aktulanie związany z żadną transakcją to zostanie utworzona nowa transakcja, która zostanie zakończona wraz z zakończeniem wykonania metody. To najczęsciej używany atrybut.

RequiresNew - Kontener zawsze tworzy nową transakcję przed wywołaniem metody ziarna EJB. Jeśli klient wykonuje inną transakcję to jest ona zawieszana. Przydatne gdy chcemy by efekty wykonania metody nie cofnęły się, np. przy logowaniu.

NotSupported - Jeśli klient wykonuje transakcję to jest ona zawieszana na czas wołania metody ziarna EJB. Jeśli nie wykonuje to nic się nie dzieje. Przydatne jeśli wykonawca transakcji nie jest obsługiwany przez J2EE JTA i J2EE nie ma kontroli nad jego transkacją.

Supports - Jeśli klient wykonuje akurat transakcję to wołanie metody ziarna EJB odbędzie się w jej kontekście, a jeśli nie to odbędzie się ono bez transakcji. Użycie tej metody jest ryzykowne, bo nie wiemy co się stanie.

Mandatory - Klient musi wywołać metodę ziarna w swojej transakcji, bo jeśli tak nie jest podnosi się wyjątek javax.transaction.TransactionRequiredException. Przydatne do testowania czy jesteśmy w transakcji.

Never - Metoda z ziarna EJB wykonuje się bez transakcji, a jeśli klient wykonuje transakcję to podnoszony jest wyjątek java.rmi.RemoteException. Przydatne do testowania czy jesteśmy w transakcji.

Najlepiej tłumaczy to rysunek. Ziarno 1 wykonuje metodę A w której woła metodę B ziarna 2:

Atrybuty transakcji i ich zasięg
Atrybut transakcji Transakcja klienta Metoda ziarana EJB
Required
BrakT2
T1T1
RequiresNew
BrakT2
T1T2
Mandatory
BrakBłąd
T1T1
NotSupported
BrakBrak
T1Brak
Supports
BrakBrak
T1T1
Never
BrakBrak
T1Błąd

Budowa pliku XML z deskryptorem aplikacji:

	<enterprise-beans>
	<session>
	...
	<transaction-type>Container</transaction-type>
	...
	</sesion>
	</enterprise-beans>
	...
	<container-transaction>
	<method>
	  //definicja metody
	</method>
	<trans-attribute>Required | ReqiresNew | ... </trans-attribute>
	</container-transaction>



4.2 Pozostałe cechy obsługi przez kontener

Domyślnym atrybutem transakcji obsługiwanych przez kontener jest Reqired. Jego obsługa wymusza na platformie J2EE duże narzuty czasowe. W związku z tym, kiedykolwiek programista jest pewien, że poziom ochrony gwarantowany przez Reqired jest za silny warto obniżyć go do innego, zmieniając atrybuty transakcji.

Zarządzanie przez kontener umożliwia także wycofywanie transakcji. Transakcje obsługiwane przez kontener możemy przerywać poprzez:

  1. wywołanie metody setRollbackOnly(); z interfejsu EJBContext
  2. wyjątek systemowy ( np. EJBException )
Transakacja jest wycofywana. Transakcja jest wycofywana także jeśli nie dojdzie do skutku przez określony w pliku konfiguracyjnym J2EE default.properties timeout (element transaction.timeout). W Sunowskiej implementacji JTA za domyślny przyjęto timeout zerowy, oznaczający że czas czekania na powodzenie transakcji (od jej rozpoczęcia) jest nieskończony.

Kontener rozpoczyna transakcję tuż przed wywołaniem metody ziarna EJB, a kończy tuż przed jej zakończeniem.



4.3 Uwagi

Jeśli wycofywana jest transakcja zmieniająca zmienne instancyjne ziarna sesyjnego to ich stan nie jest przywracany. Aby mimo wszysko osiągnąćten efekt należy zaimplementować inetrfejs SessionSynchronization, a w szczególności jego metody afterBegin() i afterCompletion(). Są one wołane odpowiednio przed i po rozpoczęciu transakcji i programista może zadbać w nich o przywrócenie odpowiednich wartości zmiennym instancyjnym

W transakcjach zarządzanej przez kontener nie wolno używać metod commit, setAutoCommit, oraz rollback z interfejsu java.sql.Connection, metody getUserTransaction z interfejsu javax.ejb.EJBContext oraz metod z interfejsu javax.transaction.UserTransaction.

Sun zaleca stosowanie tam gdzie tylko można automatycznej obsługi transakcji przez kontener.



4.4 Zalety i wady automatycznego zarządzania transakcjami

Zalety:

Wady:




5 Transakcje zarządzane na poziomie ziarna ( Bean-managed ) - JTA

Transakcje zarządzane na poziomie ziarna to transakcje JDBC i JTA. Ponieważ jednak transakcje JDBC zależą od konkretnego dostawcy bazy danych lepiej jest używać tansakcji JTA. Transakcje JTA są zarządzane i koordynowane przez platformę J2EE. Pojedyńcza transakcja JTA może obejmować wiele komponentów i systemów EIS ( baz danych ). Transakcje JTA są automatycznie propagowane pomiędzy wszystkimi bytami w nie zaangażowanymi, programista nie musi w tym celu robić nic. W transakcji uczestniczyć może więc np. kilka servletów lub stron JSP oraz kilka ziaren EJB, które z kolei mogą komunikować się z relacyjnymi bazami danych. Ręczne użycie transakcji JTA nie jest możliwe przez ziarna encyjne, a jedynie sesyjne i message-driven.

5.1 Wersja

Najświeższą dostępną wersją specyfikacji JTA jest wersja 1.0.1 pochodząca z kwietnia 1999.



5.2 Jak używać JTA ?

Wyjaśnimy to na przykładzie operacji pobrania gotówki z Bankomatu:
#import javax.transaction.*

public void pobierzKase(double ile) {	
	
   UserTransaction ut = ejbContext.getUserTransaction();	
	
   try {	
      ut.begin();
      stanRachunku -= ile;
      wyplacGotowke(ile);	
      ut.commit();	
   } catch (Exception ex) {	
       try {	
          ut.rollback();	
       } catch (SystemException syex) {	
           throw new EJBException	
              ("Rollback failed: " + syex.getMessage());	
       }	
       throw new EJBException 	
          ("Transaction failed: " + ex.getMessage());	
    }	
}

Ziarno EJB najpierw pobiera obiekt reprezentujący transakcyjność, ut. Następnie rozpoczynana jest transakcja ( metoda begin()). Po operacjach bankomatowych następuje zatwierdzenie transakcji ( metoda commit ). Jeśli w którymkolwiek momencie coś pójdzie nie tak zostanie wygenerowany wyjątek, który przechwycimy i obsłużymy wykonując rollback.



5.3 Interfejs UserTransaction

Aby skorzystać z transakcji JTA w aplikacji klienckiej lub zieranie EJB należy pobrać referencję do obiektu interfejsu UserTransaction z kontekstu w którym się znajdujemy ( czasem trzeba w tym celu użyć JNDI )

Później można już korzystać z funkcji oferowanych przez JTA. Są to:



5.4 Inne interfejsy

Oprócz interfejsu UserTransaction JTA udostępnia także 2 inne interfejsy. Są to: interfejs TransactionManager umożliwiający serwerowi aplikacji kontrolę transakcji ( programista - łapki precz ) oraz Transaction którego obiekty reprezentują transakcje. W skład Java Transaction API wchodzi ponadto pakiet javax.transaction.xa udostępniający funkcje standardu XA opartego na specyfikacji X/Open CAE.



5.5 Poziomy Izolacji

Ręczna obsługa transakcji umożliwia zmienianie poziomu izolacji transakcji JTA. To jakie poziomy są dostępne zależy od bazy danych którą mamy pod spodem i wymaga dostandaryzowania, co prawdopodobnie zostanie uczynione w następnej wersji specyfikacji, jednak generalnie dostępne są następujące poziomy:

ReadCommitted Transakcje mogą czytać jedynie dane zatwierdzone.

RepeatableRead jw, a ponadto wielokrotne czytanie tych samych danych przez transakcje na pewno zwróci te same dane.

Serializable - jw. a ponadto wielokrotne wykonanie zapytania w sytuacji gdy czytane dane się zmieniły daje te same wyniki.

Poziomu izolacji tranasakcji nie wolno zmieniać w czasie transakcji. Przykład zmiany poziomu izolacji transakcji:

   Connection con = ... ;
   con.setTransactionIsolation(TRANSACTION_READ_COMMITTED);
 



5.6 Uwagi

W transakcjach JTA zabronione jest używanie metod: getRollbackOnly(), setRollbackOnly z interfejsu EJBContext (tych metod wolno używać jedynie przy sterowaniu transakcjami przez kontener ). W transakcjach zarządzanych przez ziarno należy zamiast tego posłużyć się metodami getStatus i rollback z interfejsu UserTransaction.



5.7 Zalety i wady ręcznego zarządzania transakcjami

Zalety:

Wady:




6 Testy porównawcze

Na Akademii Górniczo Hutniczej przeprowadzono testy sprawdzające szybkość różnych rodzajów transakcji. Testowano szybkość wykonania 100 operacji przelewu bankowego, z których każda składała się z pobrania pieniędzy z jednego konta i przelania ich na inne konto:

 public void transferToSaving(double amount) throws InsufficientBalanceException { 

      checkingBalance -= amount;
      savingBalance += amount;

      try {
         updateChecking(checkingBalance);
         if (checkingBalance < 0.00) {
            context.setRollbackOnly();
            throw new InsufficientBalanceException();
         }
         updateSaving(savingBalance);
      } catch (SQLException ex) {
          throw new EJBException 
             ("Transaction failed due to SQLException: " 
             + ex.getMessage());
      }
   }
Wyniki tych testów były następujące:
 

1. Container-M. Tr.;

2. Bean-M. Tr. JDBC;

3. Bean-M. Tr. JTA

Elapsed time for 100 methods _transferToSaving_

20570[ms]

19540[ms]

19700[ms]

Przeprowadzono także testy porównawcze dwóch aplikacji, mające na celu zbadanie narzutu czasowego spowodowanego ustawieniem metody transferToSaving jako transakcyjnej i nietransakcyjnej. Obie aplikacje zawierały 'Session Bean' i transakcje były zarządzane przez kontener. Testy zakończyły się następującymi wynikami:

  metoda transakcyjna; metoda nietransakcyjna
Elapsed time for 100 methods _transferToSaving_:

20570[ms]

12940[ms]

Testy jasno pokazują, że użycie JTA bądź JDBC zamiast zarządzania transakcjami przez kontener jest niewarte poniesionego wysiłku i ryzyka popełnienia błędu oraz że tam gdzie można tego uniknąć lepiej nie stosować transakcyjności.




7 Java Transaction Service



7.1 Wersja

Aktualnie dostępną wersją specyfikacji JTS jest wersja 1.1 z listpada 1997.



7.2 Czym jest JTS ?

JTS jest Sunowską implementacją zarządcy rozproszonych transakcji. Czuwa on nad odpowiednim ich przebiegiem a jego metody są udostępniane poprzez Java Transaction API. Jest więc on niskopoziomową biblioteką do obsługi transakcji. Programista Javy nie ma potrzeby korzystania z JTS.



7.3 Jak działa JTS ?

Java Transaction Service ( JTS ) pełni rolę koordynatora transakcji dla wszystkich komponentów architektury EJB. W terminologii JTS koordynator ten nazywany jest transaction managerem. Byty chronione przez transakcje takie jak bazy danych są natomiast nazywane resource managers. Kiedy aplikacja rozpoczyna transakcję tworzy ona obiekt transakcji, reprezentujący transakcję. Następnie poszczególne resource managers są wywoływane aby wykonać ciało transakcji. Transaction manager nadzoruje pracę wszyskich resorce managers biorących udział w transakcji.

Pierwsze wołanie każdego resource managera przez aplikację identyfikuje obecną transakcję. Np. jeśli aplikacja używa relacyjnej bazy danych to gdy woła interfejs JDBC kojarzy obiekt transakcji z łączem JDBC. Później już wszystkie wołania wykorzystujące to łącze są wykonywane w imieniu transakcji bazodanowej - dopóki nie zakończy się ona.

Zazwyczaj aplikacja kończy transakcje wołaniem metody xa_commit() i transakcja jest zatwierdzana. Jeśli aplikacja z jakiegoś powodu nie może zakończyć operacji umieszczonych wewnątrz ciała transakcji to wycofuje się ona wołając xa_rollback(). Jeśli aplikacja odnosi porażkę JTS anuluje ją. Natomiast jeśli aplikacji uda się dokonać tarnsakcji to wywołuje ona JTS aby zatwierdzić transakcję. JTS wykonuje protokół zatwierdzania dwufazowego, upewniając się że zmiana została rozpropagowana i zaakceptowana przez wszystkie resurce menagery uczestniczące w transakcji.



7.4 Cechy JTS

JTS umożliwia:

Cechy JTS to:




8 Łącza do innych stron




Autor: Maciek Bagiński