2017-02-17 34 views
12

Używam Spring Boot (1.4.4.REALEASE) z danymi sprężystymi w celu zarządzania bazą danych MySql. Mam następujący przypadek:Rodzime zapytanie źródłowe wykonywane w ramach transakcji o nieaktualnej wartości

  1. Aktualizujemy jedną rewizję wykonaną w jednym urządzeniu przy użyciu RevisionService.
  2. RevisionService zapisuje wersję i wywołuje EquipmentService, aby zaktualizować stan urządzenia.
  3. Obiekt updateEquipmentStatus wywołuje procedurę składowaną Db w celu oceny sprzętu wraz z jego wersjami i aktualizacji pola.

Próbowałem niektórych opcji, ale nie udało się uzyskać zaktualizowanego statusu sprzętu. Metoda updateEquipmentStatus zapisuje poprzedni status urządzenia (nie biorąc pod uwagę aktualnej wersji zapisanej w transakcji). Kod jest napisany w ten sposób:

RevisionService

@Service 
public class RevisionService{ 

    @org.springframework.transaction.annotation.Transactional 
    public Long saveRevision(Revision rev){ 
     //save the revision using JPA-Hibernate 
     repo.save(rev); 
     equipmentService.updateEquipmentStatus(idEquipment); 
    } 
} 

EquipmentService

@Service 
public class EquipmentService{ 

    @org.springframework.transaction.annotation.Transactional 
    public Long updateEquipmentStatus(Long idEquipment){ 
     repo.updateEquipmentStatus(idEquipment); 
    } 
} 

EquipmentRepo

@Repository 
public interface EquipmentRepo extends CrudRepository<Equipment, Long> { 

    @Modifying 
    @Procedure(name = "pupdate_equipment_status") 
    void updateEquipmentStatus(@Param("id_param") Long idEquipment); 

} 

O ile rozumiem, ponieważ obie metody są opatrzone adnotacją w transakcjach Springa, metoda updateEquipmentStatus powinna zostać wykonana w zakresie bieżącej transakcji. Próbowałem również z różnymi opcjami dla opisu @Transactional z updateEquipmentStatus, takimi jak @Transactional(isolation=Isolation.READ_UNCOMMITTED) (które nie powinny być wymagane, ponieważ używam tej samej transakcji) i @Transactional(propagation=Propagation.REQUIRES_NEW), ale nadal nie biorę pod uwagę obecnego stanu. To jak mój procedura przechowywana jest zapisany w MySQL DB:

CREATE DEFINER=`root`@`localhost` PROCEDURE `pupdate_equipment_status`(IN `id_param` INT) 
    LANGUAGE SQL 
    NOT DETERMINISTIC 
    MODIFIES SQL DATA 
    SQL SECURITY DEFINER 
    COMMENT '' 
BEGIN 

/*Performs the update considering tequipment and trevision*/ 
/*to calculate the equipment status, no transaction is managed here*/ 

END 

Chciałbym także zaznaczyć, że jeśli wykonam jakąś modyfikację samego sprzętu (co wpływa tylko Tequipment), status jest odpowiednio aktualizowany. InnoDb jest silnikiem stosowanym do wszystkich tabel.


UPDATE

Wystarczy zmieniła sposób repo użyć nativeQuery zamiast i ten sam problem będzie się powtarzał, więc procedura Db angażowania należy wyrzucić:

@Modifying 
@Query(nativeQuery = true, value= "update tequipment set equipment_status = (CASE WHEN (...))") 
void updateEquipmentStatus(@Param("id_param") Long idEquipment); 

UPDATE2

Uczyniwszy więcej testów i dodaje dziennik z TransactionSynchronizationManager.getCurrentTransactionName() w sposobach, to konkretny problem:

  • Zmiany przeprowadzone w służbie urządzenia są prawidłowo zbierane przez funkcję aktualizacji (gdy coś się zmienia Tequipment status w tequipment jest obliczany prawidłowo).
  • Zmiany dokonane w usłudze rewizji (trevisie) powodują nieaktualną wartość w tequipment (nie ma znaczenia, czy Spring robi to w innej transakcji za pomocą REQUIRES_NEW, czy nie). Spring wydaje się poprawnie utworzyć nową transakcję przy użyciu REQUIRES_NEW w establishEquipmentStatus, ponieważ bieżąca nazwa transakcji zmienia się, ale zapytanie natywne nie ma ostatnich wartości (z powodu transakcji przed nieprzeprowadzeniem zobowiązania?). Próbowano także usunąć @Transactional z establishEquipmentStatus, aby ta sama transakcja była używana, ale problem nadal występuje.
  • Chciałbym, aby podświetlić, że zapytanie używane do aktualizacji statusu sprzętu ma case expression z wieloma podzapytaniami przy użyciu trevision.

Odpowiedz

0

Dodanie następującego kodu rozwiązuje go (programowo spłukiwania stan transakcji do bazy danych):

@Service 
public class EquipmentService{ 

    @PersistenceContext 
    private EntityManager entityManager; 

    @org.springframework.transaction.annotation.Transactional 
    public Long updateEquipmentStatus(Long idEquipment){ 
     entityManager.flush(); 
     repo.updateEquipmentStatus(idEquipment); 
    } 
} 

Mimo, że byłoby wspaniale, aby znaleźć deklaratywny sposób to zrobić ..

0

Zmiana na niezaakceptowany to dobry pomysł, ale trzeba również opróżnić moduł pomocy, zanim zostanie wywołana procedura składowana. Zobacz ten wątek:

How to make the queries in a stored procedure aware of the Spring Transaction?

Osobiście zrobiłbym to wszystko na wiosnę, jeśli nie jesteś absolutnie zmuszony do korzystania z procedury przechowywanej.

+0

Cześć, przepraszam za Opóźnienie. W rzeczywistości problem nie jest związany z procedurą przechowywaną, ale wydaje się, że ma więcej wspólnego z wieloma tabelami związanymi z tą samą transakcją (pobiera stare wartości z tabeli). Mniej więcej to przepływ wykonania: 1. Zaktualizuj wersję za pomocą Hibernate. 2. Wykonaj metodę "updateEquipmentStatus", która uruchamia modyfikujące natywne zapytanie (w zapytaniu tym wykorzystuje się dane związane z krokiem 1, który jest nieaktualny). –

Powiązane problemy