brak rollbacku

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

@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

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
0

DataSource z autocomitem ?

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.

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.

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.


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);

}
0

Ale po co transactional w Repository to ja nie wiem

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 ;/

0

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

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

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

3

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

A dokładnie to tu (VisitController):

@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 :

 @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.

0
jarekr000000 napisał(a):

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

A dokładnie to tu (VisitController):

@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 :

 @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

@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

//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

  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

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 2119', '2018-12-10 2149', '2018-12-10 2119', 'utf8mb4_0900_ai_ci', NULL, '', ''

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).

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 ;)

1 użytkowników online, w tym zalogowanych: 0, gości: 1