You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中@Transactional未更新缓存获取的实体至数据库问题

问题解答

首先明确:通过@Transactional实现无需手动保存实体即可更新数据库是完全可行的,但你的代码因实体状态问题导致自动同步失效。

核心原因

你从Redis缓存中获取的User实体是序列化后存储的,此时它已经脱离了Hibernate的持久化上下文,属于游离状态(Detached Entity)。Hibernate只会自动追踪**持久化状态(Managed Entity)**的实体修改,游离实体的变更不会被自动同步到数据库,哪怕标注了@Transactional也无效。

解决方法

方法1:重新加载持久化状态的User实体

从数据库重新查询User,获取处于持久化上下文的实例,再修改其子实体。事务提交时Hibernate会自动检测到变更并同步到DB:

@Transactional
public ResponseEntity<Object> updateRating(@NonNull final String sessionId, @NonNull final UserRatingUpdateRequestDTO userRatingUpdateRequestDTO) {
    final UserSession fetchedSession = userSessionService.findUserSessionBySessionId(sessionId);
    // 从DB重新加载User,纳入持久化上下文
    final User managedUser = userRepository.findById(fetchedSession.getUser().getId())
            .orElseThrow(() -> new RuntimeException("User not found"));
    final LocalDate currentDate = DateTimeUtils.dateTimeInIST().toLocalDate();
    logger.info("Updating Rating for user : {}", managedUser.getId());
    
    if (userRatingUpdateRequestDTO.isHasRated()) {
        managedUser.getUserRating().setHasGivenRating(true);
    } else {
        int weeksToAdd = 5;
        managedUser.getUserRating().setDate(currentDate.plusWeeks(weeksToAdd));
    }
    
    // 事务提交后清理缓存,避免脏读
    TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
        @Override
        public void afterCommit() {
            cacheInvalidatorService.evictUserSession(sessionId);
            cacheInvalidatorService.deleteUserDetails(managedUser.getUsername());
        }
    });
    
    return ResponseEntity.ok().body("rating updated successfully");
}

方法2:手动关联游离实体到持久化上下文(可选)

如果不想重新查询,可以用entityManager.merge(user)将游离实体转换为持久化状态,但merge会返回新的持久化实例,后续修改要基于这个实例:

@Autowired
private EntityManager entityManager;

@Transactional
public ResponseEntity<Object> updateRating(@NonNull final String sessionId, @NonNull final UserRatingUpdateRequestDTO userRatingUpdateRequestDTO) {
    final UserSession fetchedSession = userSessionService.findUserSessionBySessionId(sessionId);
    User detachedUser = fetchedSession.getUser();
    // 转换为持久化状态
    User managedUser = entityManager.merge(detachedUser);
    
    final LocalDate currentDate = DateTimeUtils.dateTimeInIST().toLocalDate();
    logger.info("Updating Rating for user : {}", managedUser.getId());
    
    if (userRatingUpdateRequestDTO.isHasRated()) {
        managedUser.getUserRating().setHasGivenRating(true);
    } else {
        int weeksToAdd = 5;
        managedUser.getUserRating().setDate(currentDate.plusWeeks(weeksToAdd));
    }
    
    TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
        @Override
        public void afterCommit() {
            cacheInvalidatorService.evictUserSession(sessionId);
            cacheInvalidatorService.deleteUserDetails(managedUser.getUsername());
        }
    });
    
    return ResponseEntity.ok().body("rating updated successfully");
}

额外注意事项

  • 确保UserRating与User的关联关系配置正确(比如@OneToOne或@ManyToOne的cascade属性),否则子实体的变更可能无法被正确追踪。
  • 缓存清理必须放在事务提交后执行,否则可能出现缓存与数据库不一致的情况。

内容的提问来源于stack exchange,提问作者pennie turci

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 08:55:18