카테고리 없음

Spring Boot : 저장소에 저장이 @Transactional (propagation = Propagation.REQUIRES_NEW)에서 작동하지 않습니다.

기록만이살길 2021. 2. 21. 08:00
반응형

Spring Boot : 저장소에 저장이 @Transactional (propagation = Propagation.REQUIRES_NEW)에서 작동하지 않습니다.

1. 질문(문제점):

내 응용 프로그램은 모듈로 나뉘어져 있으며 "사용자"모듈 관점에서 독립적 인 새로운 "등록 토큰 확인"모듈을 만들고 싶었습니다. 짐작할 수 있듯이이 모듈은 이메일 확인 토큰을 통해 사용자 계정을 활성화하는 역할을합니다. 그래서 사용자와 일대일 관계로 엔티티를 만들었습니다.

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

그런 다음 @TransactionalEventListener로 주석이 달린 리스너에서 createToken이 실행됩니다.

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

불행히도 토큰을 databse에 저장하는 것이 작동하지 않으며 쿼리가 db로 전송되지도 않습니다.

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을 사용하여 다른 메서드에 저장하는 것을 테스트했으며 작동하지만 여기에는 예외가 없기 때문에 혼란 스럽습니다. 내가 뭘 잘못하고 있니?

2. 해결방안:

단순을 사용 @Transactional하고 두 번째 방법으로 이동하면 트랜잭션이 여전히 동일하므로 작동합니다.

이전 거래에서 이미 저장 한 엔티티를 전달하고 있기 때문이라고 생각합니다. User->createToken

@Transactional 메서드는 트랜잭션이 끝날 때 데이터베이스에 자동으로 반영 되지만이 특정 트랜잭션 내에서 저장, 병합 또는 검색된 엔터티에만 적용됩니다.

일반적으로 서로 다른 트랜잭션 컨텍스트간에 엔터티를 전달하는 것은 좋은 생각이 아닙니다. 새 트랜잭션에서 필요한 경우 유일한 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);
}
65926805
반응형