2017-02-17 18 views
12

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

  1. Мы обновляем одну ревизию, выполненную на одном оборудовании, с использованием RevisionService.
  2. RevisionService сохраняет изменения и вызывает EquipmentService для обновления состояния оборудования.
  3. 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 

Я также хочу уточнить, что если я выполняю некоторые изменения в самом оборудовании (которое влияет только Tequipment), то статус должным образом обновляются. 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() в методах, что это конкретный вопрос:

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

ответ

0

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

@Service 
public class EquipmentService{ 

    @PersistenceContext 
    private EntityManager entityManager; 

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

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

0

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

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

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

+0

Привет, жаль задержка. На самом деле проблема не хранится - процедура специфична, но кажется, что она больше связана со многими таблицами, участвующими в одной и той же транзакции (она принимает значения старой таблицы). Более или менее это поток выполнения: 1. Обновите версию, используя Hibernate. 2. Выполните метод updateEquipmentStatus, который запускает модификацию собственного запроса (этот запрос использует некоторые данные, участвующие в шаге 1, который устарел). –