采用过期值

时间:2017-02-17 09:42:05

标签: spring-boot transactions spring-data spring-data-jpa

我正在使用Spring Boot(1.4.4.REALEASE)和Spring Data来管理MySql数据库。我有以下案例:

  1. 我们使用RevisionService更新在一台设备中执行的一次修订。
  2. RevisionService保存修订版并调用EquipmentService更新设备状态。
  3. updateEquipmentStatus调用Db存储过程,以便完全评估设备的修订并更新字段。
  4. 我尝试了一些选项,但没有达到获取设备的更新状态。 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)(这不应该是必需的,因为我使用相同的事务)和{{ 1}},但不考虑当前状态。这就是我的存储过程保存到MySql DB的方式:

    @Transactional(propagation=Propagation.REQUIRES_NEW)

    我还想澄清一下,如果我在设备本身中执行一些修改(仅影响设备),则状态正在正确更新。 InnoDb是用于所有表格的引擎。

    更新

    刚刚更改了repo方法以使用nativeQuery而且同样的问题一直在发生,因此涉及的Db过程应该被丢弃:

    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
    

    UPDATE2

    在方法中完成了更多测试并添加了TransactionSynchronizationManager.getCurrentTransactionName()的日志,这就是具体问题:

    • 更新功能正确选择设备服务中完成的更改(当设备中的某些内容发生变化时,设备中的状态将正确计算)。
    • 在修订服务(trevision)中完成的更改会导致设备中的过期值(如果Spring在使用REQUIRES_NEW的不同事务中执行此操作并不重要)。 Spring在@Modifying @Query(nativeQuery = true, value= "update tequipment set equipment_status = (CASE WHEN (...))") void updateEquipmentStatus(@Param("id_param") Long idEquipment); 中使用REQUIRES_NEW时似乎正确创建了一个新事务,因为当前事务名称更改,但本机查询没有最新值(因为事务未被提交之前?)。还尝试从establishEquipmentStatus删除@Transactional,因此使用相同的交易,但问题仍然存在。
    • 我想突出显示用于更新设备状态的查询具有使用trevision的多个子查询的case expression

2 个答案:

答案 0 :(得分:0)

更改为读取未提交是正确的想法,但您还需要在调用存储过程之前刷新实体管理器。见这个主题:

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

除非你绝对被迫使用存储过程,否则我会在Spring中完成所有工作。

答案 1 :(得分:0)

添加以下代码可以修复它(以编程方式将事务状态刷新到数据库):

@Service
public class EquipmentService{

    @PersistenceContext
    private EntityManager entityManager;

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

找到一种声明性的方式来做它仍然会很棒..