WstępZarzą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.
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.
|
@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 mgr = 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 |
IntroductionTransactions can be managed using the @Transactional, @UserTransactional, and @SharkTransactional annotations. We can use these annotations only in classes managed by the application context.
In classes that are not managed by the application context, we can also call our code blocks inside a single transaction using the appropriate wrapper methods from the TransactionWrapper class (from version 3.2.76). In earlier versions, use the TransactionTemplate directly.
|
@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 ); } } |
In the above example, the function without the @Transactional annotation will create separate transactions for each save function, while in the function marked @Transactional, both operations will run on the same transaction.
If you need to execute a block of code inside an active hibernate or shark transaction and have access to sessions and transactions, then you should use the TransactionWrapper class.
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 ); }); |
The code can also return any result.
It is possible to manually manage transactions in cases where we need more control or need them in a code area where we cannot use the annotations described above.
For this purpose, you can use the TransactionManagerFactory class.
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(); } } ); |
If you need to access the Session object from Hibernate, you should use TransactionTemplate as follows:
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 mgr = TransactionManagerFactory.getSharkTransactionManager(); TransactionStatus txStatus = null; try { txStatus = mgr.getTransaction( new DefaultTransactionDefinition() ); SharkTransaction sharkTransaction = mgr.getSharkTransaction(); ...//performing operations on a transaction mgr.commit( txStatus ); } catch ( Exception ex ) { mgr.rollback( txStatus ); } |
The com.suncode.pwfl.transaction.support.SharkTransactionTemplate
class facilitates Shark transaction management in a readable and secure way.
Wherever we need a Shark transaction you should use the SharkTransactionTemplate class because of its easier use. Transaction code handling can be complicated and needs to be handled carefully. |
If the code using the transaction returns a result, the call will look like this:
SharkTransactionTemplate tx = new SharkTransactionTemplate(); int result = tx.execute( new SharkTransactionCallback<Integer>() { @Override public Integer doInSharkTransaction( SharkTransaction transaction, TransactionStatus status ) throws Exception { // use of transactions return 1; } } ); |
In case you call a procedure that does not return any result, you can use another callback SharkTransactionCallbackWithoutResult
:
SharkTransactionTemplate tx = new SharkTransactionTemplate(); tx.execute( new SharkTransactionCallbackWithoutResult() { @Override public void doInSharkTransactionWithoutResult( SharkTransaction transaction, TransactionStatus status ) throws Exception { // use of transactions } } ); |
The default |