Spring - transakcje

Spring - transakcje
PA
  • Rejestracja:prawie 10 lat
  • Ostatnio:ponad 8 lat
  • Postów:80
0

Spring / Hibernate transakcje.
Kod dla zobrazowania zagadnienia:

Kopiuj
 
@Service
public class MyService implements InterfaceMyService {
    @Autowired
    private Service1 service1;

     @Autowired
    private Service2 service2;

  
   @Transactional(isolation=Isolation.SERIALIZABLE)
    public Result2 method2() {
    // some logic for getting param
  
    Integer xxxID= method3(param);
    XXX xxx = service.getObjectXXXFromDB(xxxID);
    xxx.setSomeProperty("zmiana na cos innego");

    service1.upDate(xxx);
    }

   @Transactional
   public Integer method3(Object param) {
   // some logic, save, read from DB, getting paramZ
   return service2.getXXXID(paramZ);
   }


}

  1. Jaka będzie zależność pomiędzy metodą transakcyjną method2() i method3(param). Czy wykonają się one w jednej transakcji ?
  2. Co jeśli method3(param) wykona się poprawnie a w method2() pójdzie coś nie tak ale już po wywołaniu method3(param) ? Czy będzie rollback na method3?
SZ
  • Rejestracja:prawie 11 lat
  • Ostatnio:ponad 4 lata
  • Postów:616
1
  1. Tak
  2. Jedna transakcja więc rollback na wszystko
PA
  • Rejestracja:około 9 lat
  • Ostatnio:ponad 7 lat
  • Postów:43
0

@Szczery super dzięki!
czy dla method3(param) można ustawić:

  1. Inną propagację ?
  2. Inną izolację ?

@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE)

Czy będą one respektowane ? Założenie, jest to samo - czyli dzieje się to w ramach jednej klasy na metodach publicznych.

Koziołek
Moderator
  • Rejestracja:prawie 18 lat
  • Ostatnio:około miesiąc
  • Lokalizacja:Stacktrace
  • Postów:6821
1

@Pawskii, tak możesz to ustawić, ale wywołanie musisz napisać w trochę inny sposób:

Kopiuj
public void method2(){
//...
    applicationContext.getBean(this).method3(param);
//...
}

Jest to związane z tym jak wywoływane są metody tego samego obiektu gdy wykorzystujesz proxy. W twoim kodzie masz

... → proxyService.method2() → myService.method2() → myService.method3()

wywołanie proxyService.method2() odpowiada m.in. za nałożenie transakcji. Po użyciu applicationContext

... → proxyService.method2() → myService.method2() → proxyService.method3() → myService.method3()

i wywołanie proxyService.method3() sprawdzi jak ma się sprawa transakcji i np. utworzy nową.


Sięgam tam, gdzie wzrok nie sięga… a tam NullPointerException
PA
@Koziołek Jakie będzie zachowanie jeśli nie zastosujemy takiego wywołania ? Czy metoda druga zostanie podpięta pod transakcję z pierwszej tak jak by to miało miało miejsce dla metody prywatnej ?
Koziołek
Tak. Konfiguracja tej metody zostanie zignorowana.
SZ
chyba, że skorzystamy z AspectJ
PA
  • Rejestracja:około 9 lat
  • Ostatnio:ponad 7 lat
  • Postów:43
0

@Koziołek & @Szczery dziękI!

Teraz odrobinę większy poziom złożoności

Jeszcze jedna sytuacja mnie zastanawia. Mam genereator numerów które przetrzymuję w bazie powiedzmy dla jakiś tam firm i dla oferowanych przez nich produktów.
Generator powinień zwracać unikalny numer, który przez logikę jest inkrementowany od okreslonego punktu startowego. Punkty startowe i aktualny stan przetrzymuję w bazie.

Generator działa, i testuję go w środowisku testowym dla wielu wątków.
Mam trzy konfiguracje, które różnie się zachowują i jedna z nich działa poprawnie.
Zastanawiam się skąd biorą się nieporządane rezultaty.

Wątki blokuję na wywołaniu serwisu. Poniżej opisuje 3 konfiguracje.
Pytania.
Konfiguracja I

  1. Dlaczego wystepuje ten błąd skoro tranzakcje powinny być kolekowane na wywołaniu serwisu ?
    Konfiguracja II
  2. Dlaczego ta konfiguracja działa poprawnie dopiero jak przeniosę metodę getCurrentAvailablePolicyNumberFromPool do oddzielnej klasy ? Przecież w Konfiguracji I była ona prywatna i wydaje mi się, że powinna być wykonywana w ramach adnotacji @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE) która jest nałozona na metode publiczną która ją wywołuje.
  3. Dlaczego metoda getCurrentAvailablePolicyNumberFromPool dla wszystkich wątków odczytuje ten sam stan z bazy?

Zagadnienia wydają się złożone i będą super wdzięczny za pomoc w zrozumieniu.

KONFIGURACJA I

Kopiuj
 
@Service
@Scope("singleton")
@Transactional
public class SerwisNumerów {

@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE)
    public int getCurrentAvailablePolicyNumber(Integer insuranceCompanyId, Integer insuranceProductId) throws PoolNumberException {

        int currentAvailablePolicyNumberFromPool;
        try {
            lock.lock();
            int currentAvailablePoolId = getCurrentAvailablePoolId(insuranceCompanyId, insuranceProductId);
            currentAvailablePolicyNumberFromPool = getCurrentAvailablePolicyNumberFromPool(currentAvailablePoolId, insuranceCompanyId, insuranceProductId);
        } catch (PoolNumberException e) {
            throw new PoolNumberException(e.toString());
        } finally {
            lock.unlock();
        }
        return currentAvailablePolicyNumberFromPool;
    }


   private int getCurrentAvailablePoolId(int insuranceCompanyId, int insuranceProductId){
   //zapytanie do bazy danych
   return result;
}

  private int  getCurrentAvailablePolicyNumberFromPool(int currentAvailablePoolId, int insuranceCompanyId, int insuranceProductId){
  //operacja na bazie, zmiana danych , wywołanie innych metod prywatnych
  return number;
}

}

Kopiuj
 

@Test(groups = "pool" , threadPoolSize = 3, invocationCount = 1000 , timeOut = 5000)
public void shouldReturnUniqieNumber(){
//sprawdzanie poprawnosci zwracanych numerow z generatora 
}


Powyższy test ma na celu sprawdzanie poprawnosci generowanych numerów w środowisku wielowątkowym.
W powyższej konfiguracji raz na jakiś dla kilku wywołań mam takie exception:

optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction

Numery generują się poprawnie ale jest ich za mało o tyle, ile pojawiło się błędów dla watków.

KONFIGURACJA II

Kopiuj
 
@Service
@Scope("singleton")
@Transactional
public class SerwisNumerów {

@Override
    public int getCurrentAvailablePolicyNumber(Integer insuranceCompanyId, Integer insuranceProductId) throws PoolNumberException {

        int currentAvailablePolicyNumberFromPool;
        try {
            lock.lock();
            int currentAvailablePoolId = getCurrentAvailablePoolId(insuranceCompanyId, insuranceProductId);
            currentAvailablePolicyNumberFromPool = getCurrentAvailablePolicyNumberFromPool(currentAvailablePoolId, insuranceCompanyId, insuranceProductId);
        } catch (PoolNumberException e) {
            throw new PoolNumberException(e.toString());
        } finally {
            lock.unlock();
        }
        return currentAvailablePolicyNumberFromPool;
    }


   private int getCurrentAvailablePoolId(int insuranceCompanyId, int insuranceProductId){
   //zapytanie do bazy danych
   return result;
}



}

Kopiuj
 
public class ODRĘBNA_KLASA{

@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE)
  private int  getCurrentAvailablePolicyNumberFromPool(int currentAvailablePoolId, int insuranceCompanyId, int insuranceProductId){
  //operacja na bazie, zmiana danych , wywołanie innych metod prywatnych
  return number;
}
}
}


W konfiguracji II, metodę getCurrentAvailablePolicyNumberFromPool genrrująca numery wyniosłem od innej klasy i dodałem adnotację @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE) jednocześnie usuwając ją z miejsca w którym jest wywoływana przeniesiona matego.

W POWYŻSZYM WARIANCIE WSZYSTKO DZIAŁA POPRAWNIE.

KONFIGURACJA III

Kopiuj
 
@Service
@Scope("singleton")
@Transactional
public class SerwisNumerów {

@Override
    public int getCurrentAvailablePolicyNumber(Integer insuranceCompanyId, Integer insuranceProductId) throws PoolNumberException {

        int currentAvailablePolicyNumberFromPool;
        try {
            lock.lock();
            int currentAvailablePoolId = getCurrentAvailablePoolId(insuranceCompanyId, insuranceProductId);
            currentAvailablePolicyNumberFromPool = getCurrentAvailablePolicyNumberFromPool(currentAvailablePoolId, insuranceCompanyId, insuranceProductId);
        } catch (PoolNumberException e) {
            throw new PoolNumberException(e.toString());
        } finally {
            lock.unlock();
        }
        return currentAvailablePolicyNumberFromPool;
    }


   private int getCurrentAvailablePoolId(int insuranceCompanyId, int insuranceProductId){
   //zapytanie do bazy danych
   return result;
}

@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE)
  public int  getCurrentAvailablePolicyNumberFromPool(int currentAvailablePoolId, int insuranceCompanyId, int insuranceProductId){
  //operacja na bazie, zmiana danych , wywołanie innych metod prywatnych
  return number;
}

}

Dla testu, w tej konfiguracji wszystkie wątki odczytują ten sam numer z bazy danych w tym samym czasie, przez co logika która inkrementuje numer w każdym wątku modyfikuje ten sam zaczytany stan i zwraca identyczny rezultat dla wszystkich wątków.

edytowany 1x, ostatnio: Pawskii
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)