반응형

Multiple DataSource

설명

  • 한 애플리케이션에서 여러 MySQL 서버에 접근하여 사용해야할 경우에 대하여 설명한다.
  • 아래 예제에서는 각 MySQL 서버를 포트로 구분한다.(ex. 13306, 23306)
  • 13306 포트의 MySQL 서버는 일반적인 데이터(ex. Student)를 저장하는 DB로 사용.
  • 23306 포트의 MySQL 서버는 보조적인 데이터(ex. Logging)를 저장하는 DB로 사용.
  • @Transactional 메소드 안에서 데이터소스가 다른 레파지토리들을 사용할 경우 올바르게 트랜잭션 처리가 될 수 있도록 ChainedTransactionManager, JtaTransactionManager와 같은 방법을 사용해야 한다.
  • 아래 예제에서는 JPA를 활용하기 때문에 필요하면 아래 링크 참고

테이블 생성

  • 13306 포트의 MySQL에 테이블 생성
    CREATE TABLE IF NOT EXISTS Student
    (
        studentSeq BIGINT NOT NULL AUTO_INCREMENT,
        name       VARCHAR(200),
        age        INTEGER,
        PRIMARY KEY (studentSeq)
    );
    
  • 23306 포트의 MySQL에 테이블 생성
    CREATE TABLE IF NOT EXISTS Logging
    (
        logSeq  BIGINT NOT NULL AUTO_INCREMENT,
        message VARCHAR(200),
        PRIMARY KEY (logSeq)
    );
    

application.properties 설정

...

# MySQL1
spring.datasource.main.jdbc-url=jdbc:mysql://localhost:13306/test?characterEncoding=UTF-8&serverTimezone=Asia/Seoul&useSSL=false
spring.datasource.main.username=test
spring.datasource.main.password=test
spring.datasource.main.driver-class-name=com.mysql.cj.jdbc.Driver

# MySQL2
spring.datasource.logging.jdbc-url=jdbc:mysql://localhost:23306/test?characterEncoding=UTF-8&serverTimezone=Asia/Seoul&useSSL=false
spring.datasource.logging.username=test
spring.datasource.logging.password=test
spring.datasource.logging.driver-class-name=com.mysql.cj.jdbc.Driver

Student 관련 코드 추가

  • Entity 추가
    package com.example.student.entity;
    ...
    
    @AllArgsConstructor
    @NoArgsConstructor
    @Getter
    @Setter
    @Entity
    public class Student {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long studentSeq;
        private String name;
        private Integer age;
    }
    
  • Repository 추가
    package com.example.student.repository;
    ...
    
    public interface StudentRepository extends JpaRepository<Student, Long> {
    }
    
  • Config 추가
    @EnableJpaRepositories(
        basePackages = {"com.example.student.repository"},
        entityManagerFactoryRef = "mainEntityManagerFactory",
        transactionManagerRef = "mainTransactionManager"
    )
    @Configuration
    public class MainDatabaseConfig {
        @ConfigurationProperties(prefix = "spring.datasource.main")
        @Bean
        public DataSource mainDataSource() {
            return DataSourceBuilder.create().type(HikariDataSource.class).build();
        }
    
        @Bean
        public LocalContainerEntityManagerFactoryBean mainEntityManagerFactory(@Qualifier("mainDataSource") DataSource mainDataSource) {
            LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
            entityManager.setDataSource(mainDataSource);
            entityManager.setPackagesToScan("com.example.student.entity");
            entityManager.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
            return entityManager;
        }
    
        @Bean
        public PlatformTransactionManager mainTransactionManager(@Qualifier("mainEntityManagerFactory") EntityManagerFactory mainEntityManagerFactory) {
            return new JpaTransactionManager(mainEntityManagerFactory);
        }
    }
    

Logging 관련 코드 추가

  • Entity 추가
    package com.example.logging.entity;
    ...
    
    @AllArgsConstructor
    @NoArgsConstructor
    @Getter
    @Setter
    @Entity
    public class Logging {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long logSeq;
        private String message;
    }
    
  • Repository 추가
    package com.example.logging.repository;
    ...
    
    public interface LoggingRepository extends JpaRepository<Logging, Long> {
    }
    
  • Config 추가
    @EnableJpaRepositories(
        basePackages = {"com.example.logging.repository"},
        entityManagerFactoryRef = "loggingEntityManagerFactory",
        transactionManagerRef = "loggingTransactionManager"
    )
    @Configuration
    public class LoggingDatabaseConfig {
        @ConfigurationProperties(prefix = "spring.datasource.logging")
        @Bean
        public DataSource loggingDataSource() {
            return DataSourceBuilder.create().type(HikariDataSource.class).build();
        }
    
        @Bean
        public LocalContainerEntityManagerFactoryBean loggingEntityManagerFactory(@Qualifier("loggingDataSource") DataSource loggingDataSource) {
            LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
            entityManager.setDataSource(loggingDataSource);
            entityManager.setPackagesToScan("com.example.logging.entity");
            entityManager.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
            return entityManager;
        }
    
        @Bean
        public PlatformTransactionManager loggingTransactionManager(@Qualifier("loggingEntityManagerFactory") EntityManagerFactory loggingEntityManagerFactory) {
            return new JpaTransactionManager(loggingEntityManagerFactory);
        }
    }
    

Test

@SpringBootTest
public class MultipleDataSourceTest {
    @Autowired
    private StudentRepository studentRepository;

    @Autowired
    private LoggingRepository loggingRepository;

    @Test
    public void test() {
        studentRepository.save(new Student(null, "john", 25));
        loggingRepository.save(new Logging(null, "Hello World"));

        Student dbStudent = studentRepository.findAll().stream().findFirst().get();
        Logging dbLogging = loggingRepository.findAll().stream().findFirst().get();

        assertEquals("john", dbStudent.getName());
        assertEquals("Hello World", dbLogging.getMessage());
    }
}

참고

반응형

'Development > Spring' 카테고리의 다른 글

[Spring] Spring Security  (0) 2021.05.29
[Spring] spring-retry  (0) 2021.05.27
[Spring] Testcontainers  (0) 2021.04.15
[Spring] Server Sent Event(SSE)  (0) 2021.03.30
[Spring] H2 Database  (0) 2021.02.27

+ Recent posts