-
Spring data mongodb multiple database config 설정하기Java, Kotlin, Spring 2022. 6. 24. 14:33
Spring data mongodb 기본 구성
일반적으로 Spring data 프로젝트는 단일 데이터베이스로의 연결을 지원한다.
Spring data의 데이터베이스 접근 추상화 영역인 Repository는 단일 database 접근 Client를 사용하도록 구성되어 있다.
Repsitory
interface BookRepository : MongoRepository<BookRepository, String>
Entity
@Document("book") class BookEntity(@Id val id: String)
사용 방법
@Controller class Controller(private val bookRepository: BookRepository) { @GetMapping("/books") fun getBooks() = bookRepository.findAll() }
Springboot는 application.yml에 정의된 mongodb 속성들을 기준으로 application이 실행될 때 mongodb와의 커넥션을 만든다.
단일 mongodb database로의 연결은 이 처럼 매우 간단하다.
Spring data mongodb multiple database config 설정하기
하나의 mongodb가 아닌 여러 개의 mongodb로 연결이 필요할 때는 어떻게 설정하면 되는지 살펴보자.
위에서 Repository가 별도의 설정 없이 mongodb로 연결될 수 있는 이유는 Springboot application이 실행될 때 mongodb와의 커넥션을 만들기 때문이라고 했다.
그렇다면 먼저 Repository는 내부적으로 어떻게 mongodb와의 커넥션을 만들어서 사용하는지 살펴보자.
공식문서 를 보면 Spring과 mongodb는 MongoClient를 기준으로 연결을 맺는다. MongoClient는 mongodb에 대한 접속 정보를 가지고 TCP 연결을 맺는 역할을 한다.
@Configuration public class AppConfig { /* * Use the standard Mongo driver API to create a com.mongodb.client.MongoClient instance. */ public @Bean MongoClient mongoClient() { return MongoClients.create("mongodb://localhost:27017"); } }
MongoClient는 되도록 Spring data mongodb가 제공해주는 Factory class를 사용하여 생성하도록 하자. Spring에서 제공하는 @Repository 어노테이션이 붙은 Data Access 클래스들의 DataAccessException Hierarchy를 사용할 수 있는 등 이점이 있기 때문이다.
위의 정보를 알고나면, multiple database에 대한 연결을 어떻게 해야할 지 감이 온다.
mongodb로의 connection은 MongoClient 인스턴스가 들고 있을 것인데, 여러개의 mongodb로 연결이 필요하니
여러개의 MongoClient 인스턴스가 필요하다는 것을 알 수 있다.
여러개의 MongoClient 인스턴스를 생성하여 Spring IOC 컨테이너에 담아두고, 필요할 때 마다 사용하면 된다.
MongoTemplate
Spring data는 DB와의 직접적인 연결을 맺는 MongoClient의 생성을 직접 처리하고, MongoClient를 생성해주는 좀 더 높은 추상화 Level을 제공한다.
MongoTemplate는 Spring의 MongoDB 지원의 중심적인 클래스이고, DB와 상호작용하는 풍부한 기능을 제공해준다.
CRUD 기능이 모두 포함되어 있고, Domain object와 MongoDB의 documents를 매핑해주는 기능을 제공한다.
MongoTemplate 객체 생성하는 방법
@Configuration public class AppConfig { public @Bean MongoClient mongoClient() { return MongoClients.create("mongodb://localhost:27017"); } public @Bean MongoTemplate mongoTemplate() { return new MongoTemplate(mongoClient(), "mydatabase"); } }
이처럼 MongoTemplate는 초기화 시점에 mongoClient 인스턴스를 받는다.
따라서 Multiple mongodb connection을 위해 Spring IOC 컨테이너에 적재할 대상을 MongoTemplate로 지정하는 것이 적절해보인다.
Multiple MongoTemplate 생성하기
mongodb 데이터베이스
연결해야 할 dabase는 상품 DB, 주문 DB, 리뷰 DB로 총 3개가 있다고 가정해보자.
- product
- order
- review
application.yml에 공통 사용 정보 매핑하기
spring: data: mongodb: host: 127.0.0.1 port: 27017 username: test_user password: secret
사용해야 할 database 이름 총 3개 (order, product, review)는 application.yml에 정의해도 되고, Enum으로 관리해도 될 듯 하다.
환경 별로 변하는 정보가 아니라고 가정하고 Enum으로 관리하는 방식으로 작성해보자.
속성 매핑 객체 생성
위에서 정의한 속성 정보를 담을 객체를 만들어보자.
@ConstructorBinding @ConfigurationProperties(prefix = "spring.data.mongodb") data class MongoProperty( val host: String, val port: String, val username: String, val password: String, )
MongoTemplate Configuration 생성
상품DB Configuration
@Configuration @EnableMongoRepositories( basePackages = ["com.example.multiplemongo.repository.mongo.product"], mongoTemplateRef = "productMongoTemplate", ) class ProductMongoConfig(mongoProperty: MongoProperty) { fun mongoFactory(): SimpleMongoClientDatabaseFactory { // INFO: connectionString 기준의 연결은 authSource parameter를 반드시 필요로 한다. val connectionString = "mongodb://${mongoProperty.username}:${mongoProperty.password}@" + "${mongoProperty.host}:${mongoProperty.port}/product?authSource=admin" return SimpleMongoClientDatabaseFactory(connectionString) } @Bean("mongoTemplate", "productMongoTemplate") fun mongoTemplate(): MongoTemplate = MongoTemplate(mongoFactory()) }
connectionString을 기준으로 간단하게 객체를 생성할 수 있다. 내부적으로는 이 connectionString을 parsing하여 MongoClients 인스턴스를 생성한다.
mongoClient 인스턴스는 authenticationDatabase 정보를 필요로 하기 때문에
"?authSource=<authentication database name>" 파라미터를 추가해주어야 한다.
여기서 주목해야 할 점은 @EnableMongoRepositories 어노테이션에 정의한 mongoTemplateRef 이다.
Spring data는 @Repository 어노테이션이 붙은 인터페이스를 기준으로 자동 생성된 CRUD 기능을 제공한다.
그런데 우리는 multiple database로 접근을 하는데, 문제는 여러 Repository들이 각자 사용할 mongoTemplate 인스턴스를 어떻게 알 수 있는가 이다.
만약 해당 정보를 알 수 없다면 multiple database로 접근해야하는 상황에서는 Spring data가 제공하는 자동 생성된 CRUD 기능을 사용하지 못할 것이다.
정답은 mongoTemplateRef를 통해 bean의 이름을 지정해주면 된다.
@EnableMongoRepositoreis 어노테이션 안에 mongoTemplateRef 옵션이 존재하기 때문에 반드시 선언 시점에 옵션을 지정해 주어야 하는데, @EnableMongoRepositories 어노테이션의 타겟은 ElementType.TYPE이다. 즉, Class, Interface, Enum 선언에만 사용할 수 있다.
따라서 multiple database configuration은 각각의 DB 마다 클래스로 선언이 되어야 한다.
사실 이 이유가 아니라면 하나의 Configuration 클래스 내에 3개의 MongoTemplate Bean을 정의할 수도 있을텐데, 현재로서는 선택권이 없어 보인다.
그래서 주문DB, 리뷰DB Configuration 또한 같은 방식으로 작성해주어야 한다.
주문DB Configuration
@Configuration @EnableMongoRepositories( basePackages = ["com.example.multiplemongo.repository.mongo.order"], mongoTemplateRef = "orderMongoTemplate", ) class OrderMongoConfig(mongoProperty: MongoProperty) { fun mongoFactory(): SimpleMongoClientDatabaseFactory { // INFO: connectionString 기준의 연결은 authSource parameter를 반드시 필요로 한다. val connectionString = "mongodb://${mongoProperty.username}:${mongoProperty.password}@" + "${mongoProperty.host}:${mongoProperty.port}/order?authSource=admin" return SimpleMongoClientDatabaseFactory(connectionString) } @Bean("orderMongoTemplate") fun mongoTemplate(): MongoTemplate = MongoTemplate(mongoFactory()) }
리뷰DB Configuration
@Configuration @EnableMongoRepositories( basePackages = ["com.example.multiplemongo.repository.mongo.review"], mongoTemplateRef = "reviewMongoTemplate", ) class OrderMongoConfig(mongoProperty: MongoProperty) { fun mongoFactory(): SimpleMongoClientDatabaseFactory { // INFO: connectionString 기준의 연결은 authSource parameter를 반드시 필요로 한다. val connectionString = "mongodb://${mongoProperty.username}:${mongoProperty.password}@" + "${mongoProperty.host}:${mongoProperty.port}/review?authSource=admin" return SimpleMongoClientDatabaseFactory(connectionString) } @Bean("reviewMongoTemplate") fun mongoTemplate(): MongoTemplate = MongoTemplate(mongoFactory()) }
전부 만들어 놓고 보니, 일부 중복되는 코드가 있다.
이 부분은 나름대로의 방식으로 개선해볼 수 있겠다. 필자의 경우는 Abstract를 사용했다.
abstract class AbstractMongoConfigurer(private val mongoProperty: MongoProperty) { fun mongoFactory(database: MongoDatabase): SimpleMongoClientDatabaseFactory { // INFO: connectionString 기준의 연결은 authSource parameter를 반드시 필요로 한다. val connectionString = "mongodb://${mongoProperty.username}:${mongoProperty.password}@" + "${mongoProperty.host}:${mongoProperty.port}/${database.value}?authSource=admin" return SimpleMongoClientDatabaseFactory(connectionString) } abstract fun mongoTemplate(): MongoTemplate }
반응형'Java, Kotlin, Spring' 카테고리의 다른 글
Spring 6 Http Interface (0) 2023.04.07 Spring Data 분석하기 - 1편 소개 (0) 2022.08.11 Spring Bean 스코프 (0) 2022.06.19 Java 8 주요 업그레이드 사항 (0) 2022.05.25 JVM 메모리 구조 (0) 2022.05.20