Spring native query выполняется в транзакции, принимающей устаревшее значение
Я использую Spring Boot (1.4.4.REALEASE) с данными Spring для управления базой данных MySql. У меня есть следующий случай:
- мы обновляем одну ревизию, выполняемую в одном оборудовании, используя
RevisionService.
RevisionServiceсохраняет ревизию и вызываетEquipmentServiceдля обновления состояния оборудования.- 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.
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