SpringBoot

SpringBoot JPA + Querydsl 다중 Datasource 설정 1

심나라 2023. 11. 29. 10:34
728x90

 

SpringBoot, JPA, QueryDsl, Mysql, Gradle 환경에서 2개 이상의 DB를 이용하는 샘플소스 입니다.

 

하나의 DB를 사용할 때는 application.yml 파일에 DB에 대한 정보만 설정해 주면 자동으로 Entity 와 Repository를 스캔해서 세팅을 해줍니다. 그러나, 다중 DB를 사용하기 위해서는 JPA에서 자동으로 설정해 주던 것들을 수동으로 설정해야 합니다. 

 

다중 DB를 사용하기 위해서는 아래 3개를 설정해야 합니다.

  • Datasource 설정
  • EntityManager Bean 등록
  • QueryFactory Bean 등록

 

1. 샘플 프로젝트 패키지 구조

 

 

2. JPA 프로젝트 설정파일 (build.gradle, application.yml)

 

2.1. build.gradle 파일

buildscript {
  ext {
    queryDslVersion = "5.0.0"
  }
}

plugins {
  id 'java'
  id 'org.springframework.boot' version '2.7.5'
  id 'io.spring.dependency-management' version '1.1.4'
  // querydsl 플러그인 추가
  id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

java {
  sourceCompatibility = '11'
}

configurations {
  compileOnly {
    extendsFrom annotationProcessor
  }
}

repositories {
  mavenCentral()
}

dependencies {
  implementation 'org.springframework.boot:spring-boot-starter-data-jpa'  // JPA
  implementation 'com.querydsl:querydsl-jpa'                              // QueryDSL
  implementation 'com.querydsl:querydsl-apt'                              // QueryDSL
  implementation 'org.springframework.boot:spring-boot-starter-web'       // Web
  compileOnly 'org.projectlombok:lombok'
  runtimeOnly 'com.mysql:mysql-connector-j'
  annotationProcessor 'org.projectlombok:lombok'
  testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
  useJUnitPlatform()
}

//querydsl 추가 Start
//def querydslDir = 'src/main/generated'
def querydslDir = "$buildDir/generated/querydsl"

querydsl {
  library = "com.querydsl:querydsl-apt"
  jpa = true
  querydslSourcesDir = querydslDir
}

sourceSets {
  main {
    java {
      srcDirs = ['src/main/java', querydslDir]
    }
  }
}

compileQuerydsl{
  options.annotationProcessorPath = configurations.querydsl
}

configurations {
  querydsl.extendsFrom compileClasspath
}
//querydsl 추가 End

 

2.2. application.yml 파일

spring:
  jpa:
    hibernate:
      ddl-auto: none  # create or update or none
      use-new-id-generator-mappings: false
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    properties:
      hibernate.dialect: org.hibernate.dialect.MySQL5InnoDBDialect
  primary:
    datasource:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/{첫번째 DB}
      username: {아이디}
      password: {비밀번호}
  secondary:
    datasource:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/{두번째 DB}
      username: {아이디}
      password: {비밀번호}

 

 

3. Datasource 설정

예를 들어 2개의 Datasource 를 생성할 경우, 첫 번째 Datasource는 primaryDataSource, 두 번째 Datasource는 secondaryDataSource라고 하겠습니다.

 

3.1. 첫 번째 DB (primaryDataSource) 설정 Class - PrimaryDataSourceConfig.java

 

package com.example.multidb.config;

import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "primaryEntityManagerFactory",    // EntityManager 이름
        transactionManagerRef = "primaryTransactionManager",        // TransactionManager 이름
        basePackages = { "com.example.multidb.primary.repository" } // 첫번째 DB repository 패키지 경로
)
public class PrimaryDataSourceConfig {
    @Primary
    @Bean
    @ConfigurationProperties("spring.primary.datasource") // application.yml 에서 첫번째 DB관련 설정
    public DataSourceProperties primaryDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Primary
    @Bean
    @ConfigurationProperties("spring.primary.datasource.configuration") // DB와 관련된 설정값들의 접두사에 .configuration을 붙임.
    public DataSource primaryDataSource(@Qualifier("primaryDataSourceProperties") DataSourceProperties dataSourceProperties) {
        return dataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
    }

    @Primary
    @Bean
    public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(EntityManagerFactoryBuilder builder,
                                                                              @Qualifier("primaryDataSource") DataSource dataSource) {
        return builder
                .dataSource(dataSource)
                .packages("com.example.multidb.primary.entity") // 첫번째 DB entity 패키지 경로
                .persistenceUnit("primaryEntityManager")
                .build();
    }

    @Primary
    @Bean
    public PlatformTransactionManager primaryTransactionManager(@Qualifier("primaryEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory);
    }
}

4개의 Bean을 등록하는데, 첫 번째 DB 설정 클래스의 Bean에는 @Primary를 붙여둡니다. 

 

3.2. 두 번째 DB (secondaryDataSource) 설정 Class - SecondaryDataSourceConfig.java

package com.example.multidb.config;

import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "secondaryEntityManagerFactory",    // EntityManager 이름
        transactionManagerRef = "secondaryTransactionManager",        // TransactionManager 이름
        basePackages = { "com.example.multidb.secondary.repository" } // 두번째 DB repository 패키지 경로
)
public class SecondaryDataSourceConfig {
    @Bean
    @ConfigurationProperties("spring.secondary.datasource") // application.yml 에서 두번재 DB관련 설정
    public DataSourceProperties secondaryDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.configuration") // DB와 관련된 설정값들의 접두사에 .configuration을 붙임.
    public DataSource secondaryDataSource(@Qualifier("secondaryDataSourceProperties") DataSourceProperties dataSourceProperties) {
        return dataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(EntityManagerFactoryBuilder builder,
                                                                                @Qualifier("secondaryDataSource") DataSource dataSource) {
        return builder
                .dataSource(dataSource)
                .packages("com.example.multidb.secondary.entity") // 두번째 DB model(Entity) 패키지 경로
                .persistenceUnit("secondaryEntityManager")
                .build();
    }

    @Bean
    public PlatformTransactionManager secondaryTransactionManager(@Qualifier("secondaryEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory);
    }
}

 

3.3. JPA, Querydsl 설정 Bean 등록 - QuerydslConfig.java

package com.example.multidb.config;

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 {
    // JPA 관련 설정(primary)
    @PersistenceContext(unitName = "primaryEntityManager")
    EntityManager entityManager1;
    
    // JPA 관련 설정(secondary)
    @PersistenceContext(unitName = "secondaryEntityManager")
    EntityManager entityManager2;

    // QueryDsl 관련설정(primary)
    @Bean
    public JPAQueryFactory primaryQueryFactory() {
        return new JPAQueryFactory(entityManager1);
    }

    // QueryDsl 관련설정(secondary)
    @Bean(name="secondaryQueryFactory")
    public JPAQueryFactory secondaryQueryFactory() {
        return new JPAQueryFactory(entityManager2);
    }
}

EntityManager 와 JPAQueryFactory를 등록 하고, Bean이 필요한 곳에서 주입하여 사용합니다. 

 

3.4. 첫 번재 DB Repository 설정 - PrimaryQuerydslRepositorySupport.java

package com.example.multidb.config.querydsl;

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 PrimaryQuerydslRepositorySupport extends QuerydslRepositorySupport {
    public PrimaryQuerydslRepositorySupport(Class<?> domainClass) {
        super(domainClass);
    }

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

 

3.5. 두 번째 DB Repository 설정 - SecondaryQuerydslRepositorySupport.java

package com.example.multidb.config.querydsl;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;

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

@Configuration
public abstract class SecondaryQuerydslRepositorySupport extends QuerydslRepositorySupport {
    public SecondaryQuerydslRepositorySupport(Class<?> domainClass) {
        super(domainClass);
    }

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

 

 

"SpringBoot JPA + Querydsl 다중 Datasource 설정 2" 는 다중 Datasource 설정을 이용해서 2개 DB에 데이터를 저장 하고 조회하는 샘플입니다. 

https://ksshim.tistory.com/135

 

SpringBoot JPA + Querydsl 다중 Datasource 설정 2

https://ksshim.tistory.com/134 SpringBoot JPA + Querydsl 다중 Datasource 설정 1 SpringBoot, JPA, QueryDsl, Mysql, Gradle 환경에서 2개 이상의 DB를 이용하는 샘플소스 입니다. 하나의 DB를 사용할 때는 application.yml 파일에 D

ksshim.tistory.com

 

[테스트 프로젝트 소스 다운받기]

https://github.com/ksshim0228/multidb.git

 

GitHub - ksshim0228/multidb

Contribute to ksshim0228/multidb development by creating an account on GitHub.

github.com

 

728x90