JPA VS JDBC Template https://sundries-in-myidea.tistory.com/133
먼저, 하나의 DB를 사용할 때는 application.properties
또는 application.yml
에 DB에 대한 몇가지 설정만 해주면, 알아서 Entity와 Repository들을 스캔해서 바로 개발을 할 수 있게 세팅을 해준다.
하지만, 다중 DB를 설정하기 위해서는 JPA에서 자동으로 설정해주었던 것들을 직접 설정해주어야한다.
크게 보면 아래의 2가지 설정을 해주면 된다.
QueryFactory Bean을 등록하는 작업은 QueryDsl을 사용하는 사람만 해주면 된다.
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 첫번째 DB에 대한 설정
spring.datasource.url=jdbc:mysql://localhost:3306/{DB명}
spring.datasource.username={DB 유저}
spring.datasource.password={DB 패스워드}
# 두번째 DB에 대한 설정
spring.second-datasource.url=jdbc:mysql://localhost:3306/{또 다른 DB명}
spring.second-datasource.username={DB 유저}
spring.second-datasource.password={DB 패스워드}
server:
port: 8082
spring:
#포항
pohang:
url:-
username: -
password: -
driver-class-name: org.postgresql.Driver
#광양
kwangyang:
url: -
username: -
password: -
driver-class-name: org.postgresql.Driver
jpa:
hibernate:
ddl-auto: none
properties:
hibernate:
show_sql: false
format_sql: false
...
다중 DB를 설정할 때는, @Primary 어노테이션을 이용하여 Master가되는 DataSource를 지정해야합니다.
package com.poscodx.energykeeper.util;
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.sql.DataSource;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = "com.poscodx.energykeeper.domain.pohang.repo", // 첫번째 DB가 있는 패키지(폴더) 경로
entityManagerFactoryRef = "pohangEntityManagerFactory", // EntityManager의 이름
transactionManagerRef = "pohangTransactionManager" // 트랜잭션 매니저의 이름
)
public class PohangDataSourceConfig {
@Bean
@Primary
@ConfigurationProperties("spring.pohang") // application.properties에 작성된 DB와 관련된 설정 값들의 접두사
public DataSourceProperties pohangDatasourceProperties() {
return new DataSourceProperties();
}
@Bean
@Primary
@ConfigurationProperties("spring.pohang.configuration") // DB와 관련된 설정값들의 접두사에 .configuration을 붙여준다.
public DataSource pohangDatasource() {
return pohangDatasourceProperties()
.initializeDataSourceBuilder()
.type(HikariDataSource.class)
.build();
}
@Bean(name = "pohangEntityManagerFactory")
@Primary
public LocalContainerEntityManagerFactoryBean pohangEntityManagerFactory(EntityManagerFactoryBuilder builder) {
DataSource dataSource = pohangDatasource();
return builder
.dataSource(dataSource)
.packages("com.poscodx.energykeeper.domain.pohang.entity") // 첫번째 DB와 관련된 엔티티들이 있는 패키지(폴더) 경로
.persistenceUnit("pohangEntityManager")
.build();
}
@Bean(name = "pohangTransactionManager")
@Primary
public PlatformTransactionManager pohangTransactionManager(
final @Qualifier("pohangEntityManagerFactory") LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean
) {
return new JpaTransactionManager(localContainerEntityManagerFactoryBean.getObject());
}
}
package com.poscodx.energykeeper.util;
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.sql.DataSource;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = "com.poscodx.energykeeper.domain.kwangyang.repo", // 첫번째 DB가 있는 패키지(폴더) 경로
entityManagerFactoryRef = "kwangyangEntityManagerFactory", // EntityManager의 이름
transactionManagerRef = "kwangyangTransactionManager" // 트랜잭션 매니저의 이름
)
public class KwangyangDataSourceConfig {
@Bean
@Primary
@ConfigurationProperties("spring.kwangyang") // application.properties에 작성된 DB와 관련된 설정 값들의 접두사
public DataSourceProperties kwangyangDatasourceProperties() {
return new DataSourceProperties();
}
@Bean
@Primary
@ConfigurationProperties("spring.kwangyang.configuration") // DB와 관련된 설정값들의 접두사에 .configuration을 붙여준다.
public DataSource kwangyangDatasource() {
return kwangyangDatasourceProperties()
.initializeDataSourceBuilder()
.type(HikariDataSource.class)
.build();
}
@Bean(name = "kwangyangEntityManagerFactory")
@Primary
public LocalContainerEntityManagerFactoryBean kwangyangEntityManagerFactory(EntityManagerFactoryBuilder builder) {
DataSource dataSource = kwangyangDatasource();
return builder
.dataSource(dataSource)
.packages("com.poscodx.energykeeper.domain.kwangyang.entity") // 첫번째 DB와 관련된 엔티티들이 있는 패키지(폴더) 경로
.persistenceUnit("kwangyangEntityManager")
.build();
}
@Bean(name = "kwangyangTransactionManager")
@Primary
public PlatformTransactionManager kwangyangTransactionManager(
final @Qualifier("kwangyangEntityManagerFactory") LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean
) {
return new JpaTransactionManager(localContainerEntityManagerFactoryBean.getObject());
}
}
package com.poscodx.energykeeper.util;
import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@Configuration
public class JPAConfig {
//@PersistenceContext
//private EntityManager entityManager;
@PersistenceContext(unitName = "pohangEntityManager")
private EntityManager pohangEntityManager;
@PersistenceContext(unitName = "kwangyangEntityManager")
private EntityManager kwangyangEntityManager;
//@Bean
//public JPAQueryFactory queryFactory() {
// return new JPAQueryFactory(entityManager);
//}
/* QueryDsl 관련 설정 */
@Primary // ⭐
@Bean
public JPAQueryFactory pohangQueryFactory() {
return new JPAQueryFactory(pohangEntityManager);
}
@Bean(name="kwangyangQueryFactory")
public JPAQueryFactory kwangyangQueryFactory() {
return new JPAQueryFactory(kwangyangEntityManager);
}
}
단일 datasource 설정과 다르게 다중 datasource 는 spring.datasource 뒤에 접두사 primary 와 secondary 로 구분하고 datasource 를 초기화 할때 사용하게 된다.
server:
port: 8082
spring:
datasource:
#포항
pohang:
jdbc-url: -
username: -
password: -
driver-class-name: org.postgresql.Driver
#광양
kwangyang:
jdbc-url: -
username: -
password: -
driver-class-name: org.postgresql.Driver
...
package com.poscodx.energykeeper.util;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
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 org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
@Configuration
public class DataSourceConfig {
//포항
@Primary
@Bean
@ConfigurationProperties(prefix = "spring.datasource.pohang")
public DataSource pohangDataSource() {
return DataSourceBuilder.create().build();
}
//광양
@Bean
@ConfigurationProperties(prefix = "spring.datasource.kwangyang")
public DataSource kwangyangDataSource() {
return DataSourceBuilder.create().build();
}
//포항
@Bean
public JdbcTemplate pohangJdbcTemplate(@Qualifier("pohangDataSource") DataSource pohangDataSource) {
return new JdbcTemplate(pohangDataSource);
}
//광양
@Bean
public JdbcTemplate kwangyangJdbcTemplate(@Qualifier("kwangyangDataSource") DataSource kwangyangDataSource) {
return new JdbcTemplate(kwangyangDataSource);
}
}
@RunWith(SpringRunner.class)
@SpringBootTest
public class Chapter37ApplicationTests {
@Autowired
protected JdbcTemplate primaryJdbcTemplate;
@Autowired
protected JdbcTemplate secondaryJdbcTemplate;
@Before
public void setUp() {
primaryJdbcTemplate.update("DELETE FROM USER ");
secondaryJdbcTemplate.update("DELETE FROM USER ");
}
@Test
public void test() throws Exception {
primaryJdbcTemplate.update("insert into user(name,age) values(?, ?)", "aaa", 20);
primaryJdbcTemplate.update("insert into user(name,age) values(?, ?)", "bbb", 30);
secondaryJdbcTemplate.update("insert into user(name,age) values(?, ?)", "ccc", 20);
Assert.assertEquals("2", primaryJdbcTemplate.queryForObject("select count(1) from user", String.class));
Assert.assertEquals("1", secondaryJdbcTemplate.queryForObject("select count(1) from user", String.class));
}
}
<손프로님 아이디어>
Primary/Secondary 나누지 말고 하나의 클래스에서 Map을 bean으로 생성해서 접근하면 2개를 관리할 필요가 없음
안녕하세요. 잘 봤습니다.
저는 지금 Repository 클래스에서
@PersistenceContext
private EntityManager entityManager;
이렇게 구현하여 사용하는 단계를 보고 있는데,
올리신 글처럼 이중 DB 컨피그를 구현할 경우 Repository에서
마치 Autowired 하듯 고대로
@PersistenceContext(unitName = "pohangEntityManager")
private EntityManager entityManager;
이렇게 구현해서 사용하면 되는걸까요?