Я использую 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);
}
}
Однако было бы замечательно найти декларативный способ сделать это.