1. 개요
이 튜토리얼에서는 여러 데이터베이스가 있는 Spring Data JPA 시스템에 대한 간단한 Spring 구성을 구현합니다 .
2. 엔티티
먼저 각각 별도의 데이터베이스에 있는 두 개의 간단한 엔터티를 만들어 보겠습니다.
다음은 첫 번째 사용자 엔터티입니다.
package com.baeldung.multipledb.model.user;
@Entity
@Table(schema = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
@Column(unique = true, nullable = false)
private String email;
private int age;
}
다음은 두 번째 엔터티인 Product 입니다 .
package com.baeldung.multipledb.model.product;
@Entity
@Table(schema = "products")
public class Product {
@Id
private int id;
private String name;
private double price;
}
두 엔터티가 독립적인 패키지에도 배치되어 있음을 볼 수 있습니다 . 이는 구성으로 이동할 때 중요합니다.
3. JPA 저장소
다음으로 두 개의 JPA 저장소인 UserRepository를 살펴보겠습니다 .
package com.baeldung.multipledb.dao.user;
public interface UserRepository
extends JpaRepository<User, Integer> { }
및 ProductRepository :
package com.baeldung.multipledb.dao.product;
public interface ProductRepository
extends JpaRepository<Product, Integer> { }
서로 다른 패키지에 이 두 저장소를 어떻게 생성했는지 다시 한 번 유의하십시오.
4. Java로 JPA 구성
이제 실제 Spring 구성에 도달할 것입니다. 먼저 두 개의 구성 클래스를 설정합니다. 하나는 사용자용 이고 다른 하나는 제품 용입니다 .
각 구성 클래스에서 User 에 대해 다음 인터페이스를 정의해야 합니다 .
- 데이터 소스
- EntityManagerFactory ( 유저엔티티매니저 )
- 트랜잭션매니저 ( userTransactionManager )
사용자 구성부터 살펴보겠습니다.
@Configuration
@PropertySource({ "classpath:persistence-multiple-db.properties" })
@EnableJpaRepositories(
basePackages = "com.baeldung.multipledb.dao.user",
entityManagerFactoryRef = "userEntityManager",
transactionManagerRef = "userTransactionManager"
)
public class PersistenceUserConfiguration {
@Autowired
private Environment env;
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean userEntityManager() {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(userDataSource());
em.setPackagesToScan(
new String[] { "com.baeldung.multipledb.model.user" });
HibernateJpaVendorAdapter vendorAdapter
= new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto",
env.getProperty("hibernate.hbm2ddl.auto"));
properties.put("hibernate.dialect",
env.getProperty("hibernate.dialect"));
em.setJpaPropertyMap(properties);
return em;
}
@Primary
@Bean
public DataSource userDataSource() {
DriverManagerDataSource dataSource
= new DriverManagerDataSource();
dataSource.setDriverClassName(
env.getProperty("jdbc.driverClassName"));
dataSource.setUrl(env.getProperty("user.jdbc.url"));
dataSource.setUsername(env.getProperty("jdbc.user"));
dataSource.setPassword(env.getProperty("jdbc.pass"));
return dataSource;
}
@Primary
@Bean
public PlatformTransactionManager userTransactionManager() {
JpaTransactionManager transactionManager
= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
userEntityManager().getObject());
return transactionManager;
}
}
@Primary 로 bean 정의에 주석을 달아 우리의 기본 TransactionManager 로 userTransactionManager를 사용하는 방법에 주목하십시오 . 이름으로 트랜잭션 관리자를 지정하지 않고 암시적으로 또는 명시적으로 트랜잭션 관리자를 주입하려고 할 때마다 유용합니다.
다음으로 유사한 bean을 정의하는 PersistenceProductConfiguration에 대해 논의하겠습니다 .
@Configuration
@PropertySource({ "classpath:persistence-multiple-db.properties" })
@EnableJpaRepositories(
basePackages = "com.baeldung.multipledb.dao.product",
entityManagerFactoryRef = "productEntityManager",
transactionManagerRef = "productTransactionManager"
)
public class PersistenceProductConfiguration {
@Autowired
private Environment env;
@Bean
public LocalContainerEntityManagerFactoryBean productEntityManager() {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(productDataSource());
em.setPackagesToScan(
new String[] { "com.baeldung.multipledb.model.product" });
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto",
env.getProperty("hibernate.hbm2ddl.auto"));
properties.put("hibernate.dialect",
env.getProperty("hibernate.dialect"));
em.setJpaPropertyMap(properties);
return em;
}
@Bean
public DataSource productDataSource() {
DriverManagerDataSource dataSource
= new DriverManagerDataSource();
dataSource.setDriverClassName(
env.getProperty("jdbc.driverClassName"));
dataSource.setUrl(env.getProperty("product.jdbc.url"));
dataSource.setUsername(env.getProperty("jdbc.user"));
dataSource.setPassword(env.getProperty("jdbc.pass"));
return dataSource;
}
@Bean
public PlatformTransactionManager productTransactionManager() {
JpaTransactionManager transactionManager
= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
productEntityManager().getObject());
return transactionManager;
}
}
5. 간단한 테스트
마지막으로 구성을 테스트해 보겠습니다.
이를 위해 각 엔터티의 인스턴스를 생성하고 생성되었는지 확인합니다.
@RunWith(SpringRunner.class)
@SpringBootTest
@EnableTransactionManagement
public class JpaMultipleDBIntegrationTest {
@Autowired
private UserRepository userRepository;
@Autowired
private ProductRepository productRepository;
@Test
@Transactional("userTransactionManager")
public void whenCreatingUser_thenCreated() {
User user = new User();
user.setName("John");
user.setEmail("john@test.com");
user.setAge(20);
user = userRepository.save(user);
assertNotNull(userRepository.findOne(user.getId()));
}
@Test
@Transactional("userTransactionManager")
public void whenCreatingUsersWithSameEmail_thenRollback() {
User user1 = new User();
user1.setName("John");
user1.setEmail("john@test.com");
user1.setAge(20);
user1 = userRepository.save(user1);
assertNotNull(userRepository.findOne(user1.getId()));
User user2 = new User();
user2.setName("Tom");
user2.setEmail("john@test.com");
user2.setAge(10);
try {
user2 = userRepository.save(user2);
} catch (DataIntegrityViolationException e) {
}
assertNull(userRepository.findOne(user2.getId()));
}
@Test
@Transactional("productTransactionManager")
public void whenCreatingProduct_thenCreated() {
Product product = new Product();
product.setName("Book");
product.setId(2);
product.setPrice(20);
product = productRepository.save(product);
assertNotNull(productRepository.findOne(product.getId()));
}
}
6. Spring Boot의 다중 데이터베이스
Spring Boot는 위의 구성을 단순화할 수 있습니다.
기본적으로 Spring Boot는 spring.datasource.* 접두사가 붙은 구성 속성을 사용하여 기본 DataSource를 인스턴스화합니다 .
spring.datasource.jdbcUrl = [url]
spring.datasource.username = [username]
spring.datasource.password = [password]
이제 동일한 방식으로 두 번째 DataSource를 구성 하지만 다른 속성 네임스페이스를 사용하려고 합니다 .
spring.second-datasource.jdbcUrl = [url]
spring.second-datasource.username = [username]
spring.second-datasource.password = [password]
우리는 Spring Boot 자동 구성이 이러한 다른 속성을 선택하고 두 개의 다른 DataSource를 인스턴스화하기를 원하기 때문에 이전 섹션과 유사한 두 가지 구성 클래스를 정의합니다.
@Configuration
@PropertySource({"classpath:persistence-multiple-db-boot.properties"})
@EnableJpaRepositories(
basePackages = "com.baeldung.multipledb.dao.user",
entityManagerFactoryRef = "userEntityManager",
transactionManagerRef = "userTransactionManager")
public class PersistenceUserAutoConfiguration {
@Primary
@Bean
@ConfigurationProperties(prefix="spring.datasource")
public DataSource userDataSource() {
return DataSourceBuilder.create().build();
}
// userEntityManager bean
// userTransactionManager bean
}
@Configuration
@PropertySource({"classpath:persistence-multiple-db-boot.properties"})
@EnableJpaRepositories(
basePackages = "com.baeldung.multipledb.dao.product",
entityManagerFactoryRef = "productEntityManager",
transactionManagerRef = "productTransactionManager")
public class PersistenceProductAutoConfiguration {
@Bean
@ConfigurationProperties(prefix="spring.second-datasource")
public DataSource productDataSource() {
return DataSourceBuilder.create().build();
}
// productEntityManager bean
// productTransactionManager bean
}
이제 부팅 자동 구성 규칙에 따라 persistence-multiple-db-boot.properties 내부에 데이터 소스 속성을 정의했습니다 .
흥미로운 부분은 @ConfigurationProperties 로 데이터 소스 bean 생성 방법에 주석을 다는 것 입니다 . 해당 구성 접두사를 지정하기만 하면 됩니다 . 이 메서드 내에서 DataSourceBuilder를 사용하고 있으며 Spring Boot가 자동으로 나머지를 처리합니다.
그러나 구성된 속성이 DataSource 구성 에 어떻게 삽입됩니까 ?
DataSourceBuilder 에서 build() 메서드를 호출하면 전용 bind() 메서드 가 호출됩니다 .
public T build() {
Class<? extends DataSource> type = getType();
DataSource result = BeanUtils.instantiateClass(type);
maybeGetDriverClassName();
bind(result);
return (T) result;
}
이 비공개 메서드는 자동 구성 마법의 대부분을 수행하여 해결된 구성을 실제 DataSource 인스턴스에 바인딩합니다.
private void bind(DataSource result) {
ConfigurationPropertySource source = new MapConfigurationPropertySource(this.properties);
ConfigurationPropertyNameAliases aliases = new ConfigurationPropertyNameAliases();
aliases.addAliases("url", "jdbc-url");
aliases.addAliases("username", "user");
Binder binder = new Binder(source.withAliases(aliases));
binder.bind(ConfigurationPropertyName.EMPTY, Bindable.ofInstance(result));
}
이 코드를 직접 건드릴 필요는 없지만 Spring Boot 자동 구성 내부에서 어떤 일이 발생하는지 아는 것은 여전히 유용합니다.
이 외에도 Transaction Manager 및 Entity Manager Bean 구성은 표준 Spring 애플리케이션과 동일합니다.
7. 결론
이 기사는 여러 데이터베이스를 사용하도록 Spring Data JPA 프로젝트를 구성하는 방법에 대한 실용적인 개요였습니다.
전체 구현은 GitHub 프로젝트 에서 찾을 수 있습니다 . 이것은 Maven 기반 프로젝트이므로 그대로 가져오고 실행하기 쉬워야 합니다.
출처 : https://www.baeldung.com/spring-data-jpa-multiple-databases
'Back-End > Spring' 카테고리의 다른 글
[SpringBoot] Unable to compile class for JSP / querydsl 의존성에 의한 JSP 컴파일 에러 (1) | 2023.03.10 |
---|---|
[SpringBoot] Project에서 특정 Bean을 찾지 못하는 에러 (0) | 2023.03.02 |
[Spring / Java] 이메일 인증 구현 (0) | 2023.02.20 |
[Spring Cloud] Spring Cloud Config 에서 Github Private Repository 접근하기 (0) | 2023.02.10 |
[Spring Cloud] Application의 설정 정보 (application.yml) 를 중앙에서 관리하기 (0) | 2023.02.10 |