프로젝트를 진행하는데 기존 시스템이 있는곳이라 DB를 2개 이상 사용해야된다고 합니다.
이제 Spring Boot 2.x 를 공부겸 진행하고 있는데 매번 난관이군요.
늘 생각하는거지만 실제론 쉬운 코드인데 엄청 어렵게 설명되어있는 글들이 많네요.
일단 제가 작성한대로 정리해보겠습니다.
Packacge 구조
다중 DB를 사용할 때에는 package 로 구분하게 됩니다.
customer 와 user 로 분류하여 진행하도록 하겠습니다.
-com
-project
- customer
- config
- CustomerDataSourceConfig.java
- domain
- Customer.java
- repository
- CustomerRepository.java
-user
- config
- UserDataSourceConfig.java
- domain
- User.java
- repository
- UserRepository.java
DataSourceProperties.java
application.yml
Oracle을 사용하기에 JDBC jar를 등록하여 사용했습니다.
https://gigas-blog.tistory.com/112
SpringBoot 2.x 부터 기본 jdbc 로 hikari를 사용하고 있기 때문에 url 이 아닌 jdbc-url로 수정해 주어야 합니다.
url 과 driver-class-name, username, password 는 db에 맞는 정보로 수정해야 합니다.
customer 와 user 이렇게 2개로 분리하였습니다.
server:
port: 9099
spring:
datasource:
hikari:
customer:
jdbc-url: jdbc:oracle:thin:@localhost:1521:xe
username: -
password: -
driver-class-name: oracle.jdbc.OracleDriver
user:
jdbc-url: jdbc:oracle:thin:@-:-
username: -
password: -
driver-class-name: oracle.jdbc.OracleDriver
DataSourceProperties.java
공통적인 DataSource 부분은 한곳에 정의하였습니다.
@Bean 으로 등록할 때 이름을 지정해주어 사용하려고 name을 등록해주었습니다.
@Qualifier 로 객체를 선택할 수 있도록 지정해 줍니다.
@Primary 주가 되는 DataSource를 지정해 줍니다.
@ConfigurationProperties yml이나 properties 속성을 지정해 줍니다.
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.zaxxer.hikari.HikariDataSource;
@Configuration
@EnableConfigurationProperties
public class DataSourceProperties {
@Bean(name = "customerDataSource")
@Qualifier("customerDataSource")
@Primary
@ConfigurationProperties(prefix = "spring.datasource.hikari.customer")
public DataSource customerDataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
@Bean(name = "userDataSource")
@Qualifier("userDataSource")
@ConfigurationProperties(prefix = "spring.datasource.hikari.user")
public DataSource userDataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
}
Customer.java
User.java 와 동일합니다.
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.Data;
@Entity
@Data
@Table(name = "CUSTOMER")
public class Customer {
@Id
@Column(name = "컬럼명")
private String column1;
@Column(name = "컬럼명")
private String column2;
}
CustomerRepository.java
CustomerRepository 입니다.
import org.springframework.data.jpa.repository.JpaRepository;
import com.project.customer.domain.Customer;
public interface CustomerRepository extends JpaRepository<Customer, Long>{
}
CustomerDataSourceConfig.java
주가 되는 Customer 입니다.
User와는 @Primary 차이만 있을뿐 기본적인 설정은 같습니다.
@EnableJpaRepositories의 basePackages 는 repository 경로를 설정해주면 됩니다.
customerEntityManagerFactory 의 packages 는 domain 경로를 설정해주면 됩니다.
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "customerEntityManagerFactory",
transactionManagerRef = "customerTransactionManager",
basePackages = { "com.project.customer.repository" })
public class CustomerDataSourceConfig {
@Autowired
@Qualifier("customerDataSource")
private DataSource customerDataSource;
@Primary
@Bean(name = "customerEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean customerEntityManagerFactory(EntityManagerFactoryBuilder builder) {
return builder
.dataSource(customerDataSource)
.packages("com.project.customer.domain")
.persistenceUnit("customer")
.build();
}
@Primary
@Bean("customerTransactionManager")
public PlatformTransactionManager customerTransactionManager(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(customerEntityManagerFactory(builder).getObject());
}
}
User.java
User.java 를 생성하였습니다.
테이블명과 컬럼명, 변수명은 DB에 맞게 맞춰주면 됩니다.
필요에 맞게 @GeneratedValue(strategy=GenerationType.AUTO) 를 생성해주시면 됩니다.
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.Data;
@Entity
@Data
@Table(name = "USER")
public class User {
@Id
@Column(name = "컬럼명")
private String column1;
@Column(name = "컬럼명")
private String column2;
}
UserRepository.java
UserRepository 입니다.
import org.springframework.data.jpa.repository.JpaRepository;
import com.project.user.domain.User;
public interface UserRepository extends JpaRepository<User, String>{
}
UserDataSourceConfig.java
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "userEntityManagerFactory",
transactionManagerRef = "userTransactionManager",
basePackages = { "com.project.user.repository" })
public class UserDataSourceConfig {
@Autowired
@Qualifier("userDataSource")
private DataSource userDataSource;
@Bean(name = "userEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean userEntityManagerFactory(EntityManagerFactoryBuilder builder) {
return builder
.dataSource(userDataSource)
.packages("com.project.user.domain")
.persistenceUnit("user")
.build();
}
@Bean("userTransactionManager")
public PlatformTransactionManager userTransactionManager(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(userEntityManagerFactory(builder).getObject());
}
}
@Primary 설정으로 @Transactional 처리의 기준이 됩니다.
기본 설정이 아닌 DB 의 Transaction을 처리 하려면 @Transactional(value = "userTransactionManager") 이런식으로 사용하면 됩니다.
간단하게 test를 해보았습니다.
각각의 package의 기준으로 분리된 db 를 조회해서 정상적으로 출력이 된다면 잘 적용된것을 알 수 있습니다.
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.project.customer.repository.CustomerRepository;
import com.project.user.repository.UserRepository;
import lombok.extern.slf4j.Slf4j;
@RunWith(SpringRunner.class)
@Slf4j
@SpringBootTest
public class SpringBootMultipleJpaApplicationTests {
@Autowired
private UserRepository userRepository;
@Autowired
private CustomerRepository customerRepository;
@Test
public void userTest() {
userRepository.findAll()
.stream()
.map(i -> i.toString())
.forEach(log::info);
}
@Test
public void custoemrTest(){
customerRepository.findAll()
.stream()
.map(i -> i.toString())
.forEach(log::info);
}
}
부가적으로 설명해야할 부분이 많지만 기본적인 지식을 갖고 있는 분을 기준으로 작성했으며 다른 속성들은 문의 주시면 감사하겠습니다.
'프로그래밍 > Spring' 카테고리의 다른 글
[Spring] Spring Security에서 'X-Frame-Options'응답 헤더 설정 (0) | 2019.05.07 |
---|---|
[Spring] Thymeleaf Layout 상속 (0) | 2019.05.03 |
[Spring] Thymeleaf Template 를 사용한 JavaMailSender (0) | 2019.04.28 |
[Spring] Spring Boot Hibernate dialect (0) | 2019.04.25 |
[Spring] Spring Boot 2.0 Gradle War 배포 (4) | 2019.04.25 |