서로 다른 DB를 사용하는 Service에서 QueryDSL을 사용하여 조회 메서드를 만든 후 테스트를 해보았다.
@Service
@RequiredArgsConstructor
public class PokemonService {
private final JPAQueryFactory queryFactory;
public List<PokemonEntity> selectPokemon() {
return queryFactory
.selectFrom(pokemonEntity)
.fetch();
}
}
@Service
@RequiredArgsConstructor
public class ItemService {
private final JPAQueryFactory queryFactory;
public List<ItemEntity> selectItems() {
return queryFactory
.selectFrom(itemEntity)
.fetch();
}
@Autowired
PokemonService pokemonService;
@Autowired
ItemService itemService;
@Test
void firstDB() {
List<PokemonEntity> pokemonEntities = pokemonService.selectPokemon();
pokemonEntities.forEach(ent -> System.out.println("FIRST DB QueryDSL : " + ent.getPokemonId() + " : " + ent.getName()));
}
@Test
void secondDB() {
List<ItemEntity> itemEntities = itemService.selectItems();
itemEntities.forEach(ent -> System.out.println("SECOND DB QueryDSL : " + ent.getPrice() + ", " + ent.getStockQuantity()));
}
테스트를 돌려보니 firstDB는 성공했지만 secondDB는 실패했다. 왜일까?
디버깅을 해보면 JPAQueryFactory에 첫 번째 DB의 정보가 셋팅된 EntityManager가 들어간 것을 알 수 있다.
스프링은 싱글톤으로 Bean을 생성하기 때문에 다중 DB를 사용할 경우, 어떤 DB 설정을 선택해야할 지 모른다.
때문에 주로 사용하는 DB의 설정에 @Primary 어노테이션을 사용하여 우선적으로 사용하게끔 한다.
따라서 별도로 지정해주지 않는 이상, @Primary가 붙은 DB 설정의 EntityManager를 사용한다.
JPAQueryFactory에서 DB설정을 별도로 사용하기 위해서 unitName을 이용해 각각 EntityManager를 만든다.
각 EntityManager를 주입한 JPAQueryFactory에 @Qualifier로 이름을 붙여준다.
이후 해당 @Qualifier를 사용하여 JPAQueryFactory를 주입받아 사용하면 되는데, @Primary를 붙여주면 @Qualifier를 사용하지 않을 경우 해당 JPAQueryFactory를 주입하게 된다.
기존
@Configuration
public class JpaConfig {
@Bean
JPAQueryFactory jpaQueryFactory(EntityManager em) {
return new JPAQueryFactory(em);
}
}
변경
@Configuration
public class JpaConfig {
@PersistenceContext(unitName = "firstEntityManager")
private EntityManager firstEntityManager;
@PersistenceContext(unitName = "secondEntityManager")
private EntityManager secondEntityManager;
@Bean
@Primary
@Qualifier("FirstQF")
JPAQueryFactory firstQueryFactory() {
return new JPAQueryFactory(firstEntityManager);
}
@Bean
@Qualifier("SecondQF")
JPAQueryFactory secondQueryFactory() {
return new JPAQueryFactory(secondEntityManager);
}
}
사용 시에는 다음과 같이 생성자를 만들어 사용하면 된다.
그런데 Lombok을 사용하면 될 걸 사용하는 곳마다 일일이 생성자 코드를 만들어 줘야할까?
@Service
public class ItemService {
private final JPAQueryFactory secondQueryFactory;
public ItemService(@Qualifier("SecondQF") JPAQueryFactory secondQueryFactory) {
this.secondQueryFactory = secondQueryFactory;
}
public List<ItemEntity> selectItems() {
return secondQueryFactory
.selectFrom(itemEntity)
.fetch();
}
}
그래서 코드를 이렇게 바꾸어 보니
@Service
@RequiredArgsConstructor
public class ItemService {
@Qualifier("SecondQF")
private final JPAQueryFactory queryFactory;
public List<ItemEntity> selectItems() {
return queryFactory
.selectFrom(itemEntity)
.fetch();
}
}
Lombok은 생성자를 만들 때 @Qualifier을 복사하지 않는다고 한다.
class파일을 확인해보니 정말 @Qualifier가 없는 것을 볼 수 있었다. 이러면 @Primary가 붙은 JPAQueryFactory가 주입이 되어버릴 것이다. 그럼 정말 Lombok을 사용하여 생성자를 만들 수는 없는 것일까?
그러기 위해서 Lombok 설정을 추가해주면 된다.
src/main/java 폴더 아래에 lombok.config 파일을 만든다.
그 후, 아래 설정을 추가해준다.
lombok.copyableannotations += org.springframework.beans.factory.annotation.Qualifier
설정을 추가하고 나면 더이상 경고가 뜨지 않는다.
다시 build를 해보면 @Qualifier가 추가된 것을 볼 수 있다.
테스트도 무사히 통과 😎
이것으로 기존에 목표했던 다중DB / JPA / MyBatis / QueryDSL 사용 설정을 모두 완료했다! 😆
'좌충우돌 개발기! > DB 설정' 카테고리의 다른 글
[Spring Boot] (6)스프링부트 2.x / 3.x 에서 DB 사용 예제 (0) | 2023.08.24 |
---|---|
[Spring Boot] (4) 스프링부트 3에서의 QueryDSL 설정 변경 (0) | 2023.08.23 |
[Spring Boot] (3) MyBatis 3에서의 DB 설정 (0) | 2023.08.22 |
[Spring Boot] (2) MyBatis 셋팅 변경 (1) | 2023.08.21 |
[Spring Boot] (1) DB 사용을 위한 셋팅 구성 (0) | 2023.08.20 |