brak rollbacku

MA
  • Rejestracja:około 9 lat
  • Ostatnio:ponad 5 lat
  • Postów:105
0

Dlaczego to mi nie rollbackuje xD . Uprościłem tę metodę maksymalnie tylko po to żeby zobaczyć czy kiedy mi wyrzuci runtime exception(po dodaniu drugiej wizyty o tym samym czasie) to wywala błąd ale mimo to nie cofa z bazy danych i mam exception "Visits with such date already with database" i status 500 a mimo to w bazie danych zostaje ta krotka

Kopiuj
@EnableTransactionManagement
@Transcational
@Service
public class VisitService {
    private final static Logger logger = LoggerFactory.getLogger(VisitService.class);

    private final VisitRepository visitRepository;

    public VisitService(VisitRepository visitRepository) {
        this.visitRepository = visitRepository;
    }

    public List<Visit> findAllByDate(LocalDateTime date){
        return  visitRepository.findAllByDate(date);
    }

    @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = RuntimeException.class, propagation = Propagation.REQUIRED)
    public Visit save(@Valid Visit visit) {
        logger.info("Inside visitService");
        visit.setPayed(false);
        if (visit.getPrice().intValue() > 1000)
            visit.setDiscount(new BigDecimal(0.05));
        else visit.setDiscount(new BigDecimal(0.00));
   
        visitRepository.save(visit);
        if(findAllByDate(visit.getDate()).size()>1)
            throw new RuntimeException();

        return  null;
    }
}

istotna cześć stack trace'a

Kopiuj
2018-12-08 23:16:23.653 TRACE 3360 --- [nio-8080-exec-2] o.s.t.i.TransactionInterceptor           : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
Hibernate: insert into visits (visit_date, description, discount, payed, pet_id, price, vet_id) values (?, ?, ?, ?, ?, ?, ?)
2018-12-08 23:16:23.663 TRACE 3360 --- [nio-8080-exec-2] o.s.t.i.TransactionInterceptor           : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
2018-12-08 23:16:23.664 TRACE 3360 --- [nio-8080-exec-2] o.s.t.i.TransactionInterceptor           : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAllByDate]
Hibernate: select visit0_.id as id1_6_, visit0_.visit_date as visit_da2_6_, visit0_.description as descript3_6_, visit0_.discount as discount4_6_, visit0_.payed as payed5_6_, visit0_.pet_id as pet_id6_6_, visit0_.price as price7_6_, visit0_.vet_id as vet_id8_6_ from visits visit0_ where visit0_.visit_date=?
2018-12-08 23:16:23.685 TRACE 3360 --- [nio-8080-exec-2] o.s.t.i.TransactionInterceptor           : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAllByDate]
2018-12-08 23:16:23.685 TRACE 3360 --- [nio-8080-exec-2] o.s.t.i.TransactionInterceptor           : Completing transaction for [org.springframework.samples.petclinic.services.VisitService.save] after exception: java.lang.RuntimeException
2018-12-08 23:16:23.685 TRACE 3360 --- [nio-8080-exec-2] o.s.t.i.RuleBasedTransactionAttribute    : Applying rules to determine whether transaction should rollback on java.lang.RuntimeException
2018-12-08 23:16:23.685 TRACE 3360 --- [nio-8080-exec-2] o.s.t.i.RuleBasedTransactionAttribute    : Winning rollback rule is: RollbackRuleAttribute with pattern [java.lang.RuntimeException]
2018-12-08 23:16:23.690 DEBUG 3360 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Failed to complete request: java.lang.RuntimeException
2018-12-08 23:16:23.698 ERROR 3360 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException] with root cause

java.lang.RuntimeException: null
edytowany 8x, ostatnio: Riddle
SZ
  • Rejestracja:około 11 lat
  • Ostatnio:ponad 4 lata
  • Postów:616
0

DataSource z autocomitem ?

MA
Nie wiem jak mogę to przestawić jeśli to faktycznie to. Używam spring5 MVC i jestem nowicjuszem ;/ Ja używam spring data
S8
  • Rejestracja:ponad 6 lat
  • Ostatnio:8 miesięcy
  • Postów:63
1

Pewnie masz transakcję na każdym save którą jest od razu commitowana. Czy używasz JpaRepository? jeżeli tak to by się zgadzało.
Pokaż kod VisitRepository.

Edit: po logach widzę że to JpaRepository
Możesz spróbować dać enableDefaultTransactions = false w @EnableJpaRepositories. To wyłączy transakcje na metodach JpaRepository i pozwoli samemu ustawiać @Transactional tam gdzie się chce. Trzeba pamiętać że to ustawienie globalne.

edytowany 3x, ostatnio: Seti87
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:dzień
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4708
0

Znowu komuś magia nie działa. Dziwne, nie?

Transakcja w JPARepository nie ma znaczenia, bo przecież ta zewnętrzna (VisitService.save) jest propagowana( a raczej powinna być).
(btw. te wszystkie parametry (isolation = Isolation.READ_COMMITTED, rollbackFor = RuntimeException.class, propagation = Propagation.REQUIRED) możesz sobie odpuścić).
Dlaczego nie jest?
Nie widzę błędu w tym kawałku kodu, pokaż jak wywołujesz VisitService.save, tam chyba jest jakiś kanał.
I w ogóle dziwne jest to @EnableTransactionManagement... korzystasz ze springboot ? Bo jeśli nie to ta adnotacja jest w złym miejscu (co może powodować twój problem). Jeśli korzystasz ze springboot ... to jest niepotrzebna.


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 2x, ostatnio: jarekr000000
MA
korzystam ze spring boota. Czytałem w dokumentacji że to potrzebne
MA
  • Rejestracja:około 9 lat
  • Ostatnio:ponad 5 lat
  • Postów:105
0
Seti87 napisał(a):

Pewnie masz transakcję na każdym save którą jest od razu commitowana. Czy używasz JpaRepository? jeżeli tak to by się zgadzało.
Pokaż kod VisitRepository.

Edit: po logach widzę że to JpaRepository
Możesz spróbować dać enableDefaultTransactions = false w @EnableJpaRepositories. To wyłączy transakcje na metodach JpaRepository i pozwoli samemu ustawiać @Transactional tam gdzie się chce. Trzeba pamiętać że to ustawienie globalne.

Kopiuj

public interface VisitRepository extends Repository<Visit, Integer> {

   
    Visit save(Visit visit);

    List<Visit> findByPetId(Integer petId);

    Visit findById(Integer visitId);

    @Transactional(readOnly = true)
    List<Visit> findAllByDate(LocalDateTime date);

    Visit findByDate(LocalDateTime date);

}
S8
Przetestowałeś to o czym pisałem?
MA
tak, to działa krotka znika. Minusem jest tylko to że zaczęły mi się teraz pojawiać inne błędy w web serwise xD. To znaczy że wszędzie gdzie mam teraz zapisywanie i działanie na bazie danych muszę dopisać @Transactional, bo inaczej nie działa? Chociaż w sumie nie rozumiem dlaczego na samym początku to nie działało z defaultowego poziomu? Wielkie dzięki dobry człowieku :) Poczytałem o tym i błędów ubywa, ale bez ciebie to bym nie ruszył z miejsca ;)
MA
Za szybko przytaknąłem jednak krotka nie znika, ale projekt przesłałem na githuba
S8
A nie doałeś @Transactional na całe VisitRepository? bo w przykładzie wyżej go nie było a w projekcie na githubie jest. Ustawienie enableDefaultTransactions = false było własnie po to żeby na repozytorium nie mieć bezpośrednio żadnych @Transactional domyślnie ustawionych przez spring data. Dodając @Transactional na VisitRepository włączyłeś na nowo to co wyłączyło "enableDefaultTransactions = false".
S9
  • Rejestracja:ponad 10 lat
  • Ostatnio:6 miesięcy
  • Lokalizacja:Warszawa
  • Postów:3573
0

Ale po co transactional w Repository to ja nie wiem


"w haśle <młody dynamiczny zespół> nie chodzi o to ile masz lat tylko jak często zmienia się skład"
MA
  • Rejestracja:około 9 lat
  • Ostatnio:ponad 5 lat
  • Postów:105
0
scibi92 napisał(a):

Ale po co transactional w Repository to ja nie wiem

Tzn tam było dziwne zachowanie. Za pewne to dlatego że czegoś nie rozumiem ale w momencie kiedy robiłem findAll to w bazie danych pojawiała się dana, więc dla pewności dodałem flage że to do odczytu ale to i tak nic nie dało i za każdym razem kiedy robiłem findAll to wtedy właśnie przed save pojawiała już w bazie danych ;/

S9
  • Rejestracja:ponad 10 lat
  • Ostatnio:6 miesięcy
  • Lokalizacja:Warszawa
  • Postów:3573
0

Nie możesz pokazać całości kodu? Jak dla mnie za dużo coś kombinujesz...


"w haśle <młody dynamiczny zespół> nie chodzi o to ile masz lat tylko jak często zmienia się skład"
MA
  • Rejestracja:około 9 lat
  • Ostatnio:ponad 5 lat
  • Postów:105
0
scibi92 napisał(a):

Nie możesz pokazać całości kodu? Jak dla mnie za dużo coś kombinujesz...

https://github.com/CharlesCZ/problem

S9
Nie musisz cytowac poprzedniego postu... Chyba że odnościsz się do fragmentu
S9
  • Rejestracja:ponad 10 lat
  • Ostatnio:6 miesięcy
  • Lokalizacja:Warszawa
  • Postów:3573
0

@masterkwi: Ty ten serwis wywołujesz tylko z poziomu VisitControllera? W takim razie wywal wszystkie transactionale z niekontrolerów i daj znać :D

poza tym to @EnableJpaTransactional (czy jakoś tak) powinno być w klasie z adnotacja @Configuration


"w haśle <młody dynamiczny zespół> nie chodzi o to ile masz lat tylko jak często zmienia się skład"
edytowany 1x, ostatnio: scibi92
MA
Zrobiłem tak jak mówisz: pousuwałęm zbędne @Transactional, ale nie mogłem dać @Transactional na cały kontroller bo wywalało mi błąd że mi transakcja commitowała przy samym gecie kiedy ściągałem formularz. Dodałem też osobny config . Wstawiłem na githuba kod
S9
No i jaki jest skutek?
MA
Krotka nadal jest w bazie danych
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:dzień
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4708
3

Wyjaśnienie jest takie, że zapisujesz zupełnie gdzie indziej.

A dokładnie to tu (VisitController):

Kopiuj
@ModelAttribute("visit")
    public Visit loadPetWithVisit(@PathVariable("petId") int petId, Map<String, Object> model) {
        System.out.println("wywolanie modelAttribute!!!!!!!!!!!!!");
        Pet pet = this.pets.findById(petId);
        model.put("pet", pet);
        Visit visit = new Visit();
        pet.addVisit(visit); //I tu następuje katastrofa - (K)
        return visit;
    }

Katastrofa (K) występuje, albowiem w Pet masz :

Kopiuj
 @OneToMany(cascade = CascadeType.ALL, mappedBy = "petId", fetch = FetchType.EAGER)
  private Set<Visit> visits = new LinkedHashSet<>();

Cascade ALL spowoduje, że dodanie visit (do Pet) spowoduje jej zapisanie (kaskadowo). Nieważne, że potem robisz save w (VisitService.save) (niepotrzebnie...).

Możesz spradzić, że usunięcie linii visitRepository.save(visit); nic nie zmienia. Katastrofa była już wcześniej. A metody twojego tak zwanego kontrolera nie mają @Transactional, więc wszystko wpada do bazy.


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 2x, ostatnio: jarekr000000
MA
  • Rejestracja:około 9 lat
  • Ostatnio:ponad 5 lat
  • Postów:105
0
jarekr000000 napisał(a):

Wyjaśnienie jest takie, że zapisujesz zupełnie gdzie indziej.

A dokładnie to tu (VisitController):

Kopiuj
@ModelAttribute("visit")
    public Visit loadPetWithVisit(@PathVariable("petId") int petId, Map<String, Object> model) {
        System.out.println("wywolanie modelAttribute!!!!!!!!!!!!!");
        Pet pet = this.pets.findById(petId);
        model.put("pet", pet);
        Visit visit = new Visit();
        pet.addVisit(visit); //I tu następuje katastrofa - (K)
        return visit;
    }

Katastrofa (K) występuje, albowiem w Pet masz :

Kopiuj
 @OneToMany(cascade = CascadeType.ALL, mappedBy = "petId", fetch = FetchType.EAGER)
  private Set<Visit> visits = new LinkedHashSet<>();

Cascade ALL spowoduje, że dodanie visit (do Pet) spowoduje jej zapisanie (kaskadowo). Nieważne, że potem robisz save w (VisitService.save) (niepotrzebnie...).

Możesz spradzić, że usunięcie linii visitRepository.save(visit); nic nie zmienia. Katastrofa była już wcześniej. A metody twojego tak zwanego kontrolera nie mają @Transactional, więc wszystko wpada do bazy.

Zrobiłem tak jak mówisz tzn zmieniłem Cascade All na

Kopiuj
@OneToMany(cascade = CascadeType.REMOVE, mappedBy = "petId", fetch = FetchType.EAGER)
   private Set<Visit> visits = new LinkedHashSet<>();

a potem mimo że nadal zostawało w bazie danych to nawet

Kopiuj
//pet.addVisit(visit); 

zakomentowałem, ale i tak dana zostawała w bazie danych. Na cały controller narzuciłem @Transactional i pousuwałem ze zbędnych miejsc i dodałem nawet w kontrolerze

Kopiuj
  if(visitService.findAllByDate(visit.getDate()).size()>-1)
   TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 

a mimo to nadal zostaje w bazie danych. Proszę czy mógłbyś rzucić okiem na kod na
https://github.com/CharlesCZ/problem

edytowany 4x, ostatnio: masterkwi
Zobacz pozostały 1 komentarz
MA
Uruchamiam normalnie web serwis, mam ustawione spring.jpa.hibernate.ddl-auto=create więc baza tworzy się za każdym razem od początku. Wstawiam jedną osobę przez uzupełniony formularz. Potem wstawiam zwierzaka i ustalam dla niego wizytę, czyli ładuję formularz getem a potem uzupełniam postem i czekam na wynik(posta). W w przeglądarce wywala mi wyjątek RuntimeException sprawdzam w bazie danych czy znajduje się tam wizyta i a jakże okazuje się że jest...
jarekr000000
Sorry, ale nie moge wstawić zwierzaka. Przecież tabela PetTypes jest pusta.
jarekr000000
Ściągnąlem kod. Wstawiłem se te typy zwierzaków na testy. Odpaliłem i działa. Tyle, ze nie korzystam z MySQL tylko z H2. Nie powinno to stanowić różnicy. W każdym razie nic mi się do VISITS nie wstawia i nic tam nie zostaje.
MA
Sprawdziłem to bardzo dziwne bo faktycznie na h2 rollbackuje a na mysql już nie xD
jarekr000000
To może napisz co daje Ci komenda mysql: SHOW TABLE STATUS WHERE Name = 'VISITS'; . Czuje, że twoja baza wpadła do nas wprost z zeszłego milenium...
MA
  • Rejestracja:około 9 lat
  • Ostatnio:ponad 5 lat
  • Postów:105
0

dostaje info dotyczące tabelki visits

Name, Engine, Version, Row_format, Rows, Avg_row_length, Data_length, Max_data_length, Index_length, Data_free, Auto_increment, Create_time, Update_time, Check_time, Collation, Checksum, Create_options, Comment

'visits', 'MyISAM', '10', 'Dynamic', '1', '72', '72', '281474976710655', '4096', '0', '2', '2018-12-10 21:57:19', '2018-12-10 21:57:49', '2018-12-10 21:57:19', 'utf8mb4_0900_ai_ci', NULL, '', ''

jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:dzień
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4708
5

Wszystko jasne:
MyISAM :-)

Do poczytania
https://thecamels.org/pl/czym-sie-rozni-innodb-od-myisam/

EDIT:
I jeszcze prywatne pytanie. Jak Ci Się to udało?
Ostatni raz MyISAM to widziałem jak jakiś średni admin mi taką założył, jeszcze z kodowaniem Latin-2... Ale to było 10 lat temu.
I wtedy też byłem zdziwiony, że komuś to przyszło do głowy. (tyle, że wtedy MyISAM chyba jeszcze było domyślnie).


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 1x, ostatnio: jarekr000000
Zobacz pozostałe 2 komentarze
MA
Jak zwykle programisty xD
S9
@masterkwi: tutaj musze wyjaśnić o co chodzi. @jarekr000000 bardzo często krytykuje Springa za "magie" i że często coś powinno działac a nie działa. Troche w tym racji ma (no bo nie ma narzędznia idealnego, a za dużo Springa to tez nie zdrowo) ale tutaj okazało się że jednak to nie wina Springa. Zresztą dobrze że się wyjaśniło, bo ja już myślałem że nie umiem Springa :P
jarekr000000
@scibi92 To praktycznie nigdy nie jest wina springa. Tyle, że to jest właśnie ten poziom magii (Spring + JPA), gdzie nawet ludzie z 10 letnim doświadczeniem patrzą w kod i nie są pewni co się dzieje. Czy naprawdę chcemy tego na produkcji... (btw dziś miałem wywałke @Transaction na prod. Przepuściłen kasztana na review :/ czyli catchowanie runtimeexceptionów nic nie daje, jak się spring o nich dowie. Rollback i tak pójdzie. Oczywiste, ale weź to zobacz w kodzie. Hotfix fail :-) )
S9
@jarekr000000: trochę rozumiem o Ci chodzi, ale powiem szczerze że nawet jakby nie było tu Springa tylko jakies JDBC albo coś w tym stylu to i tak bym się pewnie nie kapnął :P
jarekr000000
Gdyby tu był jasny jawny commit i insert to problem by był znaleziony znacznie szybciej. A tak najpierw trzeba ogarnąć co w ogóle tutaj spring i JPA robią. I nawet nie bardzo wiadomo gdzie debug postawić. Ogień pośredni :/
MA
  • Rejestracja:około 9 lat
  • Ostatnio:ponad 5 lat
  • Postów:105
1
jarekr000000 napisał(a):

Wszystko jasne:
MyISAM :-)

Do poczytania
https://thecamels.org/pl/czym-sie-rozni-innodb-od-myisam/

EDIT:
I jeszcze prywatne pytanie. Jak Ci Się to udało?
Ostatni raz MyISAM to widziałem jak jakiś średni admin mi taką założył, jeszcze z kodowaniem Latin-2... Ale to było 10 lat temu.
I wtedy też byłem zdziwiony, że komuś to przyszło do głowy. (tyle, że wtedy MyISAM chyba jeszcze było domyślnie).

Jestem początkujący więc konfiguracje połączenia z bazą danych mysql zerżnąłem z neta co poskutkowało tym że w konfiguracji miałem
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect <- wymusiłem MyISAM

teraz zmieniłem na

spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL57Dialect <- mam innodb
Generalnie faktycznie w mysql teraz inno jest defaultowe ale wymusiłem co innego

Teraz to już boję się mówić że działa i muszę to posprawdzać dokładnie, ale nigdy bym na to nie wpadł(myISAM) WIELKIE DZIĘKI że ci się chciało ;)

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.