spring引导:save to repository不适用于@transactional(propagation=propagation.requires\ new)

34gzjxbg  于 2021-07-24  发布在  Java
关注(0)|答案(1)|浏览(406)

我的应用程序分为几个模块,我想创建一个新的“确认注册令牌”模块,它独立于“用户”模块的Angular 。您可以猜到,该模块负责通过电子邮件验证令牌启用用户帐户。因此,我创建了一个与用户有一对一关系的实体:

  1. @Entity
  2. public class ConfirmRegistrationToken {
  3. @Id
  4. private Long id;
  5. @MapsId
  6. @OneToOne(optional = false, fetch = FetchType.LAZY)
  7. private User user;
  8. private String token;
  9. private LocalDateTime expiresAt;
  10. // setters, getters, etc.

我正在侦听在userservice中发布的onuserregisterevent:

  1. @Transactional
  2. public UserModel register(UserRegisterModel model) {
  3. validatorsExecutor.validate(
  4. new UserRegisterValidator(model, userRepository)
  5. );
  6. User user = userRegisterMapper.toObject(model);
  7. user = userRepository.save(user);
  8. eventPublisher.publishEvent(new OnUserRegisterEvent(user));
  9. return userMapper.toModel(user);
  10. }

然后,在用@transactionaleventlistener注解的侦听器中激发createtoken:

  1. @Component
  2. public class OnUserRegisterListener {
  3. private final ConfirmRegistrationTokenGenerator tokenGenerator;
  4. @Autowired
  5. public OnUserRegisterListener(ConfirmRegistrationTokenGenerator tokenGenerator) {
  6. this.tokenGenerator = tokenGenerator;
  7. }
  8. @TransactionalEventListener
  9. public void onApplicationEvent(OnUserRegisterEvent event) {
  10. tokenGenerator.createToken(event.getUser());
  11. }
  12. }

createtoken方法被注解为@transactional(propagation=propagation.requires\u new),因为我想在新的独立事务中激发此方法:

  1. @Transactional(propagation = Propagation.REQUIRES_NEW)
  2. public void createToken(User user) {
  3. ConfirmRegistrationToken token = new ConfirmRegistrationToken();
  4. token.setUser(user);
  5. String generatedToken = UUID.randomUUID().toString();
  6. token.setToken(generatedToken);
  7. Integer expirationHours = environment.getProperty("user.confirm-registration.expiration-hours", Integer.class, 24);
  8. token.setExpiresAt(LocalDateTime.now().plus(Duration.ofHours(expirationHours)));
  9. confirmRegistrationTokenRepository.save(token);
  10. //TODO implement sending message here
  11. System.out.println("To confirm your registration, just click here: http://localhost:8080/user/" + user.getId() + "/confirmRegistration/" + generatedToken);
  12. }

但不幸的是,将令牌保存到数据库不起作用,查询甚至没有发送到数据库:

  1. Hibernate: select user0_.id as id1_3_, user0_.email as email2_3_, user0_.enabled as enabled3_3_, user0_.encoded_password as encoded_4_3_, user0_.name as name5_3_ from users user0_ where user0_.email=?
  2. Hibernate: select user0_.id as id1_3_, user0_.email as email2_3_, user0_.enabled as enabled3_3_, user0_.encoded_password as encoded_4_3_, user0_.name as name5_3_ from users user0_ where user0_.name=?
  3. Hibernate: insert into users (email, enabled, encoded_password, name) values (?, ?, ?, ?)

我已经测试过用@transactional和默认值在其他方法中保存这个令牌,它是有效的,但这里没有,我很困惑,因为这里没有任何异常。我做错什么了吗?

kkih6yb8

kkih6yb81#

如果你用的是简单的 @Transactional 进入第二种方法,事务仍然是相同的,所以它可以工作。
我认为这是因为您传递的实体已经保存在上一个事务中。 User -> createToken @事务方法在事务结束时自动反映在数据库中,但它仅适用于在此特定事务中保存、合并或检索的实体。
通常,在不同的事务上下文之间传递实体不是一个好主意。如果在新事务中需要,应该传递唯一的id并再次获取整个实体。
以下是一篇关于你的问题的好文章:https://codete.com/blog/spring-transaction-propagation-modes/
我相信这会对你有帮助。
代码示例:

  1. @Transactional(propagation = Propagation.REQUIRES_NEW)
  2. public void createToken(long userId) {
  3. User user = userRepository.findById(userId).get();
  4. ConfirmRegistrationToken token = new ConfirmRegistrationToken();
  5. token.setUser(user);
  6. String generatedToken = UUID.randomUUID().toString();
  7. token.setToken(generatedToken);
  8. Integer expirationHours = environment.getProperty("user.confirm-registration.expiration-hours", Integer.class, 24);
  9. token.setExpiresAt(LocalDateTime.now().plus(Duration.ofHours(expirationHours)));
  10. confirmRegistrationTokenRepository.save(token);
  11. //TODO implement sending message here
  12. System.out.println("To confirm your registration, just click here: http://localhost:8080/user/" + user.getId() + "/confirmRegistration/" + generatedToken);
  13. }
展开查看全部

相关问题