Spring native query выполняется в транзакции, принимающей устаревшее значение



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




  1. мы обновляем одну ревизию, выполняемую в одном оборудовании, используя RevisionService.


  2. RevisionService сохраняет ревизию и вызывает EquipmentService для обновления состояния оборудования.

  3. updateEquipmentStatus вызывает хранимую процедуру БД, чтобы оценить оборудование с его ревизиями в целом и обновить поле.


У меня есть пробовал некоторые варианты, но не достиг, чтобы получить обновленный статус для оборудования. Метод 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);

}


Насколько я понимаю, поскольку оба метода аннотированы транзакцией весны, то Метод 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-это двигатель, используемый для всех таблиц.





Обновить



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



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




UPDATE2



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




  • изменения, внесенные в сервис оборудования правильно подобран функцией обновления (когда что-то в tequipment изменяется, статус в tequipment вычисляется правильно).

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

  • я хотел бы подчеркнуть, что запрос, используемый для обновления состояния оборудования, имеет выражение case с несколькими подзапросами, использующими trevision.

690   2  

2 ответов:

Изменение на чтение uncommitted-это правильная идея, но вам также нужно будет очистить entitymanager перед вызовом хранимой процедуры. Смотрите эту нить:

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

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

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

@Service
public class EquipmentService{

    @PersistenceContext
    private EntityManager entityManager;

    @org.springframework.transaction.annotation.Transactional
    public Long updateEquipmentStatus(Long idEquipment){
        entityManager.flush();
        repo.updateEquipmentStatus(idEquipment);
    }
}
Тем не менее было бы здорово найти декларативный способ сделать это..

Comments

    Ничего не найдено.