Подтвердить что ты не робот

Spring собственный запрос, выполненный в транзакции с устаревшим значением

Я использую Spring Boot (1.4.4.REALEASE) с Spring данными для управления базой данных MySql. У меня есть следующий случай:

  • Мы обновляем одну ревизию, выполненную на одном оборудовании, с помощью RevisionService.
  • RevisionService сохраняет ревизию и вызывает EquipmentService для обновления состояния оборудования.
  • UpdateEquipmentStatus выполняет вызов хранимой процедуры Db, чтобы оценить оборудование с его изменениями и обновить поле.

Я пробовал некоторые варианты, но не добился, чтобы получить обновленный статус для оборудования. Метод updateEquipmentStatus продолжает записывать предыдущий статус для оборудования (не учитывая текущую ревизию, хранящуюся в транзакции). Код написан следующим образом:

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

}

Насколько я понимаю, поскольку оба метода аннотируются с транзакцией Spring, метод updateEquipmentStatus должен выполняться в области текущей транзакции. Я также пробовал с различными вариантами аннотации @Transactional от updateEquipmentStatus, например @Transactional(isolation=Isolation.READ_UNCOMMITTED) (что не обязательно, потому что я использую ту же транзакцию) и @Transactional(propagation=Propagation.REQUIRES_NEW), но не учитываю текущий статус. То, как моя хранимая процедура сохраняется в базе данных MySql:

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

Я также хочу пояснить, что, если я выполню некоторые изменения в самом оборудовании (которое влияет только на оборудование), статус будет соответствующим образом обновлен. InnoDb - это двигатель, используемый для всех таблиц.


UPDATE

Просто изменил метод репо, чтобы использовать nativeQuery, и эта же проблема продолжает происходить, поэтому необходимо задействовать процедуру Db:

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

UPDATE2

Сделав больше тестов и добавив в методы журнал TransactionSynchronizationManager.getCurrentTransactionName(), эта конкретная проблема:

  • Изменения, внесенные в сервисное обслуживание оборудования, должным образом выбираются функцией обновления (когда что-то меняется в телегайнере, состояние в телегинетике рассчитывается должным образом).
  • Изменения, внесенные в службу ревизий (trevision), приводят к устаревшему значению в телеобъявлении (неважно, делает ли это Spring в другой транзакции с использованием REQUIRES_NEW или нет). Spring, похоже, правильно создает новую транзакцию при использовании REQUIRES_NEW в establishEquipmentStatus, потому что текущее имя транзакции изменяется, но собственный запрос не имеет последних значений (из-за транзакции до того, как она не была совершена?). Также попытался удалить @Transactional из establishEquipmentStatus, поэтому используется одна и та же транзакция, но проблема продолжает происходить.
  • Я хочу выделить, что запрос, используемый для обновления состояния оборудования, имеет case expression с несколькими подзапросами, используя trevision.

Update3

Добавление следующего кода исправляет его (программно сбрасывая состояние транзакции в базу данных):

@Service
public class EquipmentService{

    @PersistenceContext
    private EntityManager entityManager;

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

Однако было бы замечательно найти декларативный способ сделать это.

4b9b3361

Ответ 1

Смена чтения без фиксации - это правильная идея, но вам также нужно будет очистить сущность-менеджер до того, как будет вызвана ваша хранимая процедура. Смотрите эту тему:

Как сделать запросы в хранимой процедуре осведомленными о транзакции Spring?

Лично я сделаю все это в Spring, если вы не будете вынуждены использовать хранимую процедуру.