Spring-webflux muliti database @Trasactional 설정하기

dev-well-being·2023년 1월 10일
2
post-thumbnail
post-custom-banner

Spring-webflux에서 MongoDB, R2DBC(MySQL) 2가지 DB를 사용하고 있고 JPA처럼 @Transactional을 사용하여 트랜잭션을 처리하고 싶었다.

구글링을 통해 여러 자료를 참고하여 @Transactional을 MongoDB, R2DBC 각각 사용할 수 있도록 설정했다.

  • MongoDB transaction config
@Configuration
@EnableReactiveMongoRepositories(basePackages = "xxx.xxxxxxx.xxx.repository.mongo")
public class MongoConfig {
    @Bean
    public ReactiveMongoTransactionManager transactionManager(ReactiveMongoDatabaseFactory reactiveMongoDatabaseFactory) {
        return new ReactiveMongoTransactionManager(reactiveMongoDatabaseFactory);
    }

    @Bean
    public TransactionalOperator transactionalOperator(ReactiveTransactionManager transactionManager) {
        return TransactionalOperator.create(transactionManager);
    }
}
  • R2DBC transaction config
@Configuration
@EnableR2dbcRepositories(basePackages = "xxx.xxxxxxx.xxx.repository.r2dbc")
public class R2dbcConfig {

    @Bean
    public R2dbcTransactionManager r2dbcTransactionManager(ConnectionFactory connectionFactory) {
        return new R2dbcTransactionManager(connectionFactory);
    }
}

그리고 transaction을 테스트 해보았다.
  • MongoDB transaction test
	@Transactional
    public Mono<HelloResDto> saveHelloEx(HelloReqDto reqDto){
        Hello req = Hello.builder()
                .message(reqDto.getMessage())
                .build();
        return helloRepository.insert(req)
                .map(HelloResDto::from)
                .doOnSuccess(helloResDto -> {throw new RuntimeException();})
                ;
    }
	@Test
    void rollbackTest(){
        HelloReqDto test = HelloReqDto
                .builder()
                .message("rollback Test242343242")
                .build();

        StepVerifier
                .create(helloService.saveHelloEx(test))
                .assertNext(helloResDto -> assertThat(helloResDto.getMessage()).isEqualTo(test.getMessage()))
                .verifyComplete();
    }
  • R2DBC transaction test
	@Transactional
    public Mono<UserResDto> saveUserEx(UserReqDto userReqDto){
        User user = User.builder()
                .username(userReqDto.getUsername())
                .password(userReqDto.getPassword())
                .name(userReqDto.getName())
                .department(userReqDto.getDepartment())
                .build();
        return userRepository.save(user).map(UserResDto::from)
                .doOnSuccess(userResDto -> {throw new RuntimeException();});
    }
	@Test
    void rollbackTest(){
        UserReqDto test = UserReqDto
                .builder()
                .username("test@test.com")
                .password("test@test.com")
                .department("test@test.com")
                .name("test@test.com")
                .build();

        StepVerifier
                .create(userService.saveUserEx(test))
                .assertNext(userResDto -> assertThat(userResDto.getName()).isEqualTo(test.getName()))
                .verifyComplete();
    }

그런데 아래와 같은 오류가 발생하였다.
No qualifying bean of type 'org.springframework.transaction.TransactionManager' available: expected single matching bean but found 2: transactionManager,r2dbcTransactionManager
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.transaction.TransactionManager' available: expected single matching bean but found 2: transactionManager,r2dbcTransactionManager

MongoDB, R2DBC에 각각 설정한 transaction bean type이 동일하여 @Trasactional에서 어떤 bean을 사용할지 몰라서 발생하는 오류였다.

그래서 bean type이 충돌나지 않도록 MongoDB transaction config에 @Primary 추가하여 우선권을 주었다.

	@Bean
    @Primary
    public ReactiveMongoTransactionManager transactionManager(ReactiveMongoDatabaseFactory reactiveMongoDatabaseFactory) {
        return new ReactiveMongoTransactionManager(reactiveMongoDatabaseFactory);
    }

그러고 실행했더니 MongoDB는 정상적으로 transaction 처리가 되었는데 R2DBC는 transaction 처리가 안되는 것이였다.

둘 다 설정하는 건 안되는 것일까?

우리의 구글신을 영접하여 @Transactional별로 transactionManager bean을 선택할 수 있는 방법을 알아내었다.

value 옵션을 사용하여 transactionManager Bean을 선택할 수 있다.

	@Transactional(value = "r2dbcTransactionManager")
    public Mono<UserResDto> saveUserEx(UserReqDto userReqDto){
        User user = User.builder()
                .username(userReqDto.getUsername())
                .password(userReqDto.getPassword())
                .name(userReqDto.getName())
                .department(userReqDto.getDepartment())
                .build();
        return userRepository.save(user).map(UserResDto::from)
                .doOnSuccess(userResDto -> {throw new RuntimeException();});
    }

그리고 다시 테스트하였더니 R2DBC도 transaction이 정상적으로 수행되는 것을 확인하였다.

profile
안녕하세요!! 좋은 개발 문화를 위해 노력하는 dev-well-being 입니다.
post-custom-banner

0개의 댓글