Zarządzanie transakcjami jest możliwe za pomocą adnotacji @Transactonal, @UserTransactional, oraz @SharkTransactional. Adnotacje te możemy używać tylko w klasach zarządzanych przez kontekst aplikacji.
Zaleca się stosowanie adnotacji do zarządzania transakcjami w klasach oznaczonych adnotacją @Service |
W klasach, które nie są zarządzane przez kontekst aplikacji możemy wywoływać nasze bloki kodu również wewnątrz jednej transakcji wykorzystując do tego odpowiednie metody wrappujące z klasy TransactionWrapper (od wersji 3.2.76). We wcześniejszych wersjach należy skorzystać bezpośrednio z TransactionTemplate.
Należy pamiętać, że wszystkie operacje związane z bazą danych, które wykonujemy w danym zadaniu, powinny odbywać się wewnątrz jednej transakcji. Dzięki temu nasz kod będzie transakcyjny oraz będzie się szybciej wykonywać. |
@Service public class CustomServiceImpl implements CustomService { @Autowired private StructureService structureService; @Transactional public void addPositionAndOu() { Position p = new Position( "name", "symbol" ); OrganizationalUnit ou = new OrganizationalUnit(); ou.setName( "name" ); ou.setSymbol( "symbol" ); structureService.createPosition( p ); structureService.createOrganizationalUnit( ou ); } public void addPositionAndOuNonTransactional() { Position p = new Position( "name", "symbol" ); OrganizationalUnit ou = new OrganizationalUnit(); ou.setName( "name" ); ou.setSymbol( "symbol" ); structureService.createPosition( p ); structureService.createOrganizationalUnit( ou ); } } |
W powyższym przykładzie funkcja bez adnotacji @Transactional utworzy osobne transakcje dla każdej funkcji save, natomiast w funkcji oznaczonej @Transactional obie operacje będą działać na jednej tej samej transakcji.
Jeżeli potrzebujemy wykonać blok kodu wewnątrz aktywnej transakcji hibernate lub sharkowej oraz mieć dostęp do sesji i transakcji, to należy skorzystać z klasy TransactionWrapper.
TransactionWrapper.get().doInHibernateTransaction( ( session ) -> { QueryExecutor qe = ComponentFactory.getQueryExecutor(); SQLQuery sql = qe.createSQLQuery( ... ); ... sql.executeUpdate(); }); |
TransactionWrapper.get().doInSharkTransaction( ( sharkTransaction ) -> { ActivityService activityService = ServiceFactory.getActivityService(); Map<String, Object> activityContext = activityService.getActivityContext( processId, activityId ); activityContext.put( "id_zmiennej", "nowa_wartość" ); activityService.setActivityContext( processId, activityId, activityContext ); }); |
Nasz kod może również zwracać dowolny wynik.
Istnieje możliwość manualnego zarządzania transakcjami w przypadkach gdy potrzebujemy większej kontroli lub potrzebujemy ich w obszarze kodu, w którym nie mamy możliwości skorzystania z adnotacji opisanych powyżej.
W tym celu możemy wykorzystać klasę TransactionManagerFactory
TransactionTemplate tt = new TransactionTemplate( TransactionManagerFactory.getHibernateTransactionManager() ); tt.execute( new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult( TransactionStatus status ) { QueryExecutor qe = ComponentFactory.getQueryExecutor(); SQLQuery sql = qe.createSQLQuery( ... ); ... sql.executeUpdate(); } } ); |
Jeżeli potrzebujemy mieć dostęp do obiektu Session od Hibernate, należy TransactionTemplate wykorzysać następująco:
TransactionTemplate tt = new TransactionTemplate( TransactionManagerFactory.getHibernateTransactionManager() ); tt.execute( new SessionAwareTransactionCallbackWithoutResult() { @Override public void doWithSession( Session session ) { session.delete( ... ); } } ); |
final SharkTransactionManager mgr= TransactionManagerFactory.getSharkTransactionManager(); TransactionTemplate tt = new TransactionTemplate( mgr ); tt.execute( new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult( TransactionStatus status ) { SharkTransaction sharkTransaction = mgr.getSharkTransaction(); ... } } ); |
SharkTransactionManager= TransactionManagerFactory.getSharkTransactionManager(); TransactionStatus txStatus = null; try { txStatus = mgr.getTransaction( new DefaultTransactionDefinition() ); SharkTransaction sharkTransaction = mgr.getSharkTransaction(); ...//wykonywanie operacji na transakcji mgr.commit( txStatus ); } catch ( Exception ex ) { mgr.rollback( txStatus ); } |
Klasa com.suncode.pwfl.transaction.support.SharkTransactionTemplate
ułatwia zarządzanie transakcją Shark'ową w czytelny i bezpieczny sposób.
Wszędzie tam, gdzie potrzebujemy transakcję Shark'ową powinniśmy wykorzystywać klasę SharkTransactionTemplate ze względu na łatwiejsze jej użycie. Obsługa kodu transakcyjnego może być skomplikowana i wymaga uważnego traktowania. |
Jeżeli kod wykorzystujący transakcję zwraca wynik, wywołanie będzie wyglądać następująco:
SharkTransactionTemplate tx = new SharkTransactionTemplate(); int result = tx.execute( new SharkTransactionCallback<Integer>() { @Override public Integer doInSharkTransaction( SharkTransaction transaction, TransactionStatus status ) throws Exception { // wykorzystanie transakcji return 1; } } ); |
W przypadku, gdy wywołujemy procedurę która nie zwraca żadnego wyniku, możemy skorzystać z innego callback'u SharkTransactionCallbackWithoutResult
:
SharkTransactionTemplate tx = new SharkTransactionTemplate(); tx.execute( new SharkTransactionCallbackWithoutResult() { @Override public void doInSharkTransactionWithoutResult( SharkTransaction transaction, TransactionStatus status ) throws Exception { // wykorzystanie transakcji } } ); |
Domyślna konfiguracja |