java - Spring 启动 : save to repository doesnt work with @Transactional(propagation = Propagation. REQUIRES_NEW)

标签 java spring spring-boot transactions

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

@Entity
public class ConfirmRegistrationToken {
    @Id
    private Long id;

    @MapsId
    @OneToOne(optional = false, fetch = FetchType.LAZY)
    private User user;

    private String token;

    private LocalDateTime expiresAt;

    // setters, getters, etc.

我正在监听 UserService 中发布的 OnUserRegisterEvent:

    @Transactional
    public UserModel register(UserRegisterModel model) {
        validatorsExecutor.validate(
            new UserRegisterValidator(model, userRepository)
        );

        User user = userRegisterMapper.toObject(model);
        user = userRepository.save(user);
        eventPublisher.publishEvent(new OnUserRegisterEvent(user));
        return userMapper.toModel(user);
    }

然后,createToken 在使用 @TransactionalEventListener 注解的监听器中被触发:

@Component
public class OnUserRegisterListener {
    private final ConfirmRegistrationTokenGenerator tokenGenerator;

    @Autowired
    public OnUserRegisterListener(ConfirmRegistrationTokenGenerator tokenGenerator) {
        this.tokenGenerator = tokenGenerator;
    }

    @TransactionalEventListener
    public void onApplicationEvent(OnUserRegisterEvent event) {
        tokenGenerator.createToken(event.getUser());
    }
}

createToken 方法用 @Transactional(propagation = Propagation.REQUIRES_NEW) 注释,因为我想在新的独立事务中触发此方法:

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void createToken(User user) {
        ConfirmRegistrationToken token = new ConfirmRegistrationToken();
        token.setUser(user);

        String generatedToken = UUID.randomUUID().toString();
        token.setToken(generatedToken);

        Integer expirationHours = environment.getProperty("user.confirm-registration.expiration-hours", Integer.class, 24);
        token.setExpiresAt(LocalDateTime.now().plus(Duration.ofHours(expirationHours)));
        confirmRegistrationTokenRepository.save(token);

        //TODO implement sending message here
        System.out.println("To confirm your registration, just click here: http://localhost:8080/user/" + user.getId() + "/confirmRegistration/" + generatedToken);
    }

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

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=?
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=?
Hibernate: insert into users (email, enabled, encoded_password, name) values (?, ?, ?, ?)

我已经测试过使用 @Transactional 和默认值在其他方法中保存此 token ,它可以工作,但这里不行,我很困惑,因为这里没有任何异常。我做错了什么吗?

最佳答案

如果您使用简单的@Transactional进入第二种方法,事务仍然相同,因此它可以工作。

我相信这是因为您传递的实体已在上一个事务中保存。 User -> createToken

@Transactional方法会在事务结束时自动反射(reflect)在数据库中,但它仅适用于在此特定事务中保存、合并或检索的实体。

通常,在不同事务上下文之间传递实体并不是一个好主意。如果您在新事务中需要它,您应该传递唯一的 id 并再次获取整个实体。

这是一篇关于您的问题的好文章:https://codete.com/blog/spring-transaction-propagation-modes/

相信会对你有帮助。

代码示例:

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void createToken(long userId) {
    User user = userRepository.findById(userId).get();
    ConfirmRegistrationToken token = new ConfirmRegistrationToken();
    token.setUser(user);

    String generatedToken = UUID.randomUUID().toString();
    token.setToken(generatedToken);

    Integer expirationHours = environment.getProperty("user.confirm-registration.expiration-hours", Integer.class, 24);
    token.setExpiresAt(LocalDateTime.now().plus(Duration.ofHours(expirationHours)));
    confirmRegistrationTokenRepository.save(token);

    //TODO implement sending message here
    System.out.println("To confirm your registration, just click here: http://localhost:8080/user/" + user.getId() + "/confirmRegistration/" + generatedToken);
}

关于java - Spring 启动 : save to repository doesnt work with @Transactional(propagation = Propagation. REQUIRES_NEW),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65926805/

相关文章:

java - 如何以编程方式将已设置的 bean 注册到 spring 上下文

java - 我应该将我的 WAR 文件放在我的 Pivotal TC 服务器上的什么位置?

java - Spring Security - 不是通过 IP 而是通过域/子域进行身份验证?

java - SpringBoot 和 GitLab CI 运行测试

Java 矩形选择

java - 如何使用 Java 比较器进行排序忽略某些值

java - Android如何以编程方式绘制圆角矩形形状

java - 为什么我的测试不能从其父级继承其 ContextConfiguration 位置?

spring-boot - thymeleaf 与 thymeleaf-spring4 依赖关系

java - 从 Spring rest api 返回列表