Spring native query在transaction中执行,获取过期值

bsxbgnwa  于 2023-11-17  发布在  Spring
关注(0)|答案(3)|浏览(124)

我正在使用Sping Boot (1.4.4.REALEASE)和Spring Data来管理MySql数据库。我有以下案例:
1.我们使用RevisionService更新在一台设备中执行的一次修订。

  1. RevisionService保存修订版并调用EquipmentService更新设备状态。
  2. updateEquipmentStatus调用一个Db存储过程,以评估设备及其修订版本并更新字段。
    我已经尝试了一些选项,但无法获得设备的更新状态。updateEquipmentStatus方法继续写入设备的以前状态(不考虑当前版本存储在事务中)。代码是这样写的:

修订服务

@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);
    }
}

字符串

设备服务

@Service
public class EquipmentService{

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

设备回购

@Repository
public interface EquipmentRepo extends CrudRepository<Equipment, Long> {

    @Modifying
    @Procedure(name = "pupdate_equipment_status")
    void updateEquipmentStatus(@Param("id_param") Long idEquipment);

}


据我所知,由于这两个方法都使用Spring的transactional进行了注解,因此updateEquipmentStatus方法应该在当前transaction的范围内执行。(这不应该是必需的,因为我使用的是同一个事务)和@Transactional(propagation=Propagation.REQUIRES_NEW),但一直不考虑当前状态。这就是我的存储过程保存到MySql 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


我还想澄清的是,如果我对设备本身进行了一些修改(这只会影响tequipment),状态会被正确更新。InnoDb是用于所有表的引擎。

更新

只是将repo方法改为使用nativeQuery,同样的问题一直在发生,所以应该放弃所涉及的Db过程:

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

  • 酒店

在做了更多的测试并在方法中添加了一个带有TransactionSynchronizationManager.getCurrentTransactionName()的日志之后,这就是具体的问题:

  • 设备服务中的更改由更新功能正确选择(当设备中的某些内容发生更改时,设备中的状态将正确计算)。
  • 在修订版服务中完成的更改导致设备价值过时(Spring是否在不同的transaction中使用REQUIRES_NEW并不重要)。Spring在establishEquipmentStatus中使用REQUIRES_NEW时似乎正确地创建了一个新transaction,因为当前transaction名称更改,但本机查询没有最新值(因为没有提交之前的事务?).还尝试从establishEquipmentStatus中删除@Transactional,以便使用相同的事务,但问题一直发生。
  • 我想强调的是,用于更新设备状态的查询有一个case expression,使用trevision有多个子查询。
cedebl8k

cedebl8k1#

添加以下代码可以修复此问题(将事务状态自动刷新到数据库):

@Service
public class EquipmentService{

    @PersistenceContext
    private EntityManager entityManager;

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

字符串
不过,如果能找到一种声明性的方式来做这件事,那就太好了。

lnxxn5zx

lnxxn5zx2#

修改为read uncommitted是正确的想法,但在调用存储过程之前,您还需要刷新entitymanager。请参阅以下线程:
How to make the queries in a stored procedure aware of the Spring Transaction?
就我个人而言,我会在Spring中完成这一切,除非你绝对必须使用存储过程。

pjngdqdw

pjngdqdw3#

@Transactional
@Modifying(clearAutomatically = true)
@Query(value = "CALL PR_CALC_PAYMENT(:id_payment)", nativeQuery = true)
void reportPayment(@Param("id_payment") Long paymentId);

字符串
这对我来说很有效,这个注解更新了持久化上下文。我注意到,如果我调用调用过程的方法,因为它是一个事务,持久化上下文将不会更新,直到方法终止。
https://www.baeldung.com/spring-data-jpa-modifying-annotation

相关问题