Token实体通过TokenService加载,TokenService通过JpaRepository获取Token。我的理解是,对于这个读操作,会自动提供一个事务块。
之后,我调用PasswordService#resetPassword
,它被Spring的@Transactional
注解。先前获取的Token通过参数传递。然后它的validationTime-property被修改。
@Controller
public class UserAccountController {
@PostMapping("/reset")
public String processPasswordReset(PasswordResetDto resetDto) {
String tokenSequence = resetDto.getTokenSequence();
Token token = tokenService.findBySequence(tokenSequence);
passwordService.resetPassword(token);
return "redirect:/";
}
}
@Service
public class PasswordService {
@Transactional
public void resetPassword(Token token) {
token.setValidationTime(new Timestamp(System.currentTimeMillis()));
}
}
令人惊讶的是,在没有显式保存令牌的情况下,令牌将在离开交易块时更新。这让我假设脏检查已经完成,Hibernate会自动更新token。
我不完全理解的是,Hibernate是如何实现管理这个令牌的,尽管我没有在事务中显式地再次获取它?据我所知,当我再次获取令牌时,我将获得一级缓存版本。但这样一来,Hibernate必须以某种方式弄清楚我想要更新实体。
当缓存的实体被传递到事务块中时,Hibernate会自动重新附加实体吗?还是有什么别的事
1条答案
按热度按时间kninwzqo1#
Hibernate不会在新事务中自动重新附加实体。这很可能是因为您使用的是默认的spring-boot设置,该设置将启用“在视图中打开WeblogyManager”模式。这种模式基本上会在处理HTTP请求时开始一个事务,并在发送HTTP响应时完成该事务。
这意味着当
passwordService#resetPassword()
被执行时,事务实际上仍然是活动的,因此传递给它的Token
仍然由Hibernate管理。因此,脏检查仍然适用于它。然后,当提交事务时,Hibernate知道它是脏的,因此生成相关的SQL来更新它。令人惊讶的是,在没有显式保存令牌的情况下,令牌将在离开交易块时更新。
这是使用Hibernate的误解之一。您不需要显式调用
EntityManager
上的任何方法来更新。只要一个实体仍然被管理并且是脏的,它就会在事务提交时被更新,我从下面的文档中引用了这样的行为。由此:
处于管理/持久状态的实体可以由应用程序操纵,并且当持久性上下文被刷新时,任何改变将被自动检测和持久性。不需要调用特定的方法来使修改持久化。
由此,它将在提交事务之前默认自动刷新持久化上下文:
默认情况下,Hibernate使用刷新模式,在以下情况下触发刷新: