개발/Spring

QueryDSL + multi data source 연동하기

갓생사는 김초원의 개발 블로그 2021. 9. 13. 22:15

1.QueryDSL + single data source 인 경우

DataSourceConfiguration

- entityManagerFactory 생성 메서드

- 데이터소스 클래스를 생성하는 것이 주 내용이 아니기 때문에 entityManagerFactory 생성 메소드만 올린다. 

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(
            @Qualifier("dataSource") DataSource dataSource) {
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(dataSource);
        entityManagerFactoryBean.setPackagesToScan("com.example.rds.jpa.entity"); // entity 클래스가 있는 패키지 경로
        JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter);
        entityManagerFactoryBean.setJpaPropertyMap(hibernateProperties());
        return entityManagerFactoryBean;
    }

 

QueryDslConfiguration

- @PersistenceContext로 DataSourceConfiguration에서 생성한 EntityManager를 주입.

- JPAQueryFactory 빈 등록 

import com.querydsl.jpa.impl.JPAQueryFactory;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * The type Query dsl configuration.
 */
@Configuration
public class QueryDslConfiguration {

    @PersistenceContext
    private EntityManager entityManager;

    @Bean
    public JPAQueryFactory jpaQueryFactory() {
        return new JPAQueryFactory(entityManager);
    }
}

 

JPAQueryFactory 사용

import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;

import java.util.List;

import static com.example.db.jpa.entity.QTestInfo.testInfo;

/**
 * The type Example Test info repository support.
 */
@Repository
public class ExampleTestRepositorySupport extends QuerydslRepositorySupport {

    private final JPAQueryFactory queryFactory; // jPAQueryFactory 주입 받아 사용 

    /**
     * Instantiates a new Example test repository support.
     *
     * @param queryFactory the query factory
     */
    public ExampleTestRepositorySupport(JPAQueryFactory queryFactory) {
        super(TestInfo.class);
        this.queryFactory = queryFactory;
    }

    public List<TestInfo> findTestInfoListBySearch(long test_id) {
        return queryFactory
                .selectFrom(testInfo)
                .where(testInfo.testId.eq(test_id))
                .fetch();
    }
}

2.QueryDSL + multi data source 인 경우

datasource가 2개이고 각각 entityManagerFactory 생성 메소드가 있는 경우에 위와 같이 그대로 구현하면 아래와 같은 오류가 발생한다. 

Parameter 0 of method setEntityManager in org.springframework.data.jpa.repository.support.QuerydslRepositorySupport required a single bean, but 2 were found:
	- org.springframework.orm.jpa.SharedEntityManagerCreator#0: defined by method 'createSharedEntityManager' in null
	- org.springframework.orm.jpa.SharedEntityManagerCreator#1: defined by method 'createSharedEntityManager' in null

 

QueryDslConfiguration에서 entityManager에 객체 주입을 하는 과정에서  2개의 entityManager 팩토링 생성 메서드 중 어느 것으로 주입을 할지 몰라 생기는 오류다. 

 

아래와 같이 수정한다. 

DataSource1Configuration

    /**
     * Entity manager factory local container entity manager factory bean.
     *
     * @param dataSource the data source
     * @return the local container entity manager factory bean
     */
    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(
            @Qualifier("dataSource") DataSource dataSource) {
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(dataSource);
        entityManagerFactoryBean.setPackagesToScan("com.example.rds.jpa.entity1");
        entityManagerFactoryBean.setPersistenceUnitName("firstEntityManager"); // 영속성 객체 이름을 지정
        JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter);
        entityManagerFactoryBean.setJpaPropertyMap(hibernateProperties());
        return entityManagerFactoryBean;
    }

DataSource2Configuration

    /**
     * Entity manager factory local container entity manager factory bean.
     *
     * @param dataSource the data source
     * @return the local container entity manager factory bean
     */
    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(
            @Qualifier("dataSource") DataSource dataSource) {
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(dataSource);
        entityManagerFactoryBean.setPackagesToScan("com.example.rds.jpa.entity2");
        entityManagerFactoryBean.setPersistenceUnitName("secondEntityManager"); // 영속성 객체 이름을 지정
        JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter);
        entityManagerFactoryBean.setJpaPropertyMap(hibernateProperties());
        return entityManagerFactoryBean;
    }

 

QueryDslConfiguration

import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Configuration
public class QueryDslConfig {

    @PersistenceContext(unitName = "firstEntityManager")
    private EntityManager firstEntityManager;

    @PersistenceContext(unitName = "secondEntityManager")
    private EntityManager secondEntityManager;

    @Bean
    public JPAQueryFactory firstJpaQueryFactory() {
        return new JPAQueryFactory(firstEntityManager);
    }

    @Bean
    public JPAQueryFactory secondJpaQueryFactory() {
        return new JPAQueryFactory(secondEntityManager);
    }
}

 

커스텀  QuerydslRepositorySupprt 클래스 구현 (상속으로)

- setter로 EntityManager를 지정 

- SecondQuerydslRepositorySupprt 클래스도 마찬가지로 setter에 @PersistenceContext(unitName = "secondEntityManager") 라고 지정하여 구현하면 된다. 

import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
import org.springframework.stereotype.Repository;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Repository
public abstract class FirstQuerydslRepositorySupport extends QuerydslRepositorySupport {

    /**
     * Creates a new {@link QuerydslRepositorySupport} instance for the given domain type.
     *
     * @param domainClass must not be {@literal null}.
     */
    public FirstQuerydslRepositorySupport(Class<?> domainClass) {
        super(domainClass);
    }

    @Override
    @PersistenceContext(unitName = "firstEntityManager")
    public void setEntityManager(EntityManager entityManager) {
        super.setEntityManager(entityManager);
    }
}

 

실제 사용

- 위에서 구현한 커스텀 QuerydslRepositorySupprt을 상속받아 실세 QuertDSL을 사용하는 클래스를 구현한다. 

- JPAQueryFactory 객체 주입할 때 @Qualifier 어노테이션으로 특정 빈을 지정

import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;
import org.springframework.beans.factory.annotation.Qualifier;

import java.util.List;

import static com.example.db.jpa.entity.QTestInfo.testInfo;

/**
 * The type Example Test info repository support.
 */
@Repository
public class ExampleTestRepositorySupport extends QuerydslRepositorySupport {

    private final JPAQueryFactory queryFactory; // jPAQueryFactory 주입 받아 사용 

    /**
     * Instantiates a new Example test repository support.
     *
     * @param queryFactory the query factory
     */
    public ExampleTestRepositorySupport(@Qualifier("firstJpaQueryFactory")JPAQueryFactory queryFactory) {
        super(TestInfo.class);
        this.queryFactory = queryFactory;
    }

    public List<TestInfo> findTestInfoListBySearch(long test_id) {
        return queryFactory
                .selectFrom(testInfo)
                .where(testInfo.testId.eq(test_id))
                .fetch();
    }
}