Back-End/Spring

[JPA] Spring Boot에서 다중 DB를 사용하기

HMHA 2023. 3. 2. 09:53
300x250
반응형
300x250

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

 

Spring JPA – Multiple Databases | Baeldung

How to set up Spring Data JPA to work with multiple, separate databases.

www.baeldung.com

 

300x250
반응형