파란하늘의 지식창고
Published 2018. 12. 24. 14:18
Spring Data Mongodb 사용해보기 Study/Java
반응형

Spring Boot Mongo 설정

MongoClient를 바로 사용하는 방법과 MongoTemplate을 사용해서 Spring Data의 Repository를 사용하는 방법이 있다.

아래 dependency를 추가한다.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

각자 환경에 맞게 아래 properties를 추가한다. (만약 설정하지 않으면 아래 설정이 default로 사용된다.)

spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017

아래처럼 configuration 설정을 추가한다.

@Configuration
@EnableMongoRepositories(basePackages = "net.bluesky.blog.repository", mongoTemplateRef = "blogMongoTemplate")
@EnableMongoAuditing
public class BlogDataMongoConfig {

	@Bean
	public MongoTemplate blogMongoTemplate(MongoClient mongoClient) {
		MongoDatabaseFactory factory = new SimpleMongoClientDatabaseFactory(mongoClient, "blog");
		return new MongoTemplate(factory);
	}
}

여기까지 설정하면 Spring Data Jpa처럼 Domain, Repository를 만들고 사용할 수 있다.

MongoDB의 _id 속성

MongoDB는 Collection별로 document를 저장한다. (db의 table의 1 row를 저장하는 것과 비슷)

document별로 _id 속성이 있는데 이는 Collection에 저장된 여러 document에 대해 유일함을 식별하기 위한 기본 key이다.

별다른 선언이 없으면 ObjectId type을 사용한다.

ObjectId

ObjectId는 BSON type으로 12 byte로 이루어져 있다.

ObjectId의 구성은 다음과 같다.

ObjectId layout

0

1

2

3

4

5

6

7

8

9

10

11

time

machine

pid

inc

MongoDB는 _id를 client에서 만든다. ( 분산 database 환경 )

동일 time에 동일 machine에서 동일 process id에서 만드는 경우 0 ~ 8까지 동일한 값이 나오지만 마지막 3 byte inc가 중복이 되는 것을 막는다.

spring data mongodb에서 _id Field가 맵핑 계층에서 처리되는 방법

spring data mongodb <-> MongoDB 간 id 맵핑

How the _id Field is Handled in the Mapping Layer

@Id로 지정된 field가 있으면 MongoDB의 _id로 맵핑된다.

만약 @Id로 지정된 field가 없다면 id란 이름의 field가 MongoDB의 _id로 맵핑된다.

java domain의 field와 MongoDB의 field가 맵핑되는 예시는 다음과 같다.

Field definition

Resulting Id-Fieldname in MongoDB 

String id

_id

@Field String id

_id

@Field("x") String id

x

@Id String x

_id

@Field("x") @Id String x

_id

 

Domain 구현하기

만약 java domain의 id field가 String이나 BigInteger로 선언되면 가능한 ObjectId로 변환되어 저장된다.

java domain의 id 가 String으로 명시된 경우 Converter<String, ObjectId>를 통해 MongoDB의 _id가 ObjectId로 변환된다.

java domain의 id가 BigInteger로 명시된 경우 Converter<BigInteger, ObjectId>를 통해 MongoDB의 _id가 ObjectId로 변환된다.

id field를 ObjectId로 선언한 경우 MongoDB에 그대로 저장된다.

즉 java domain을 만들 때 String, ObjectId, BigInteger를 Id로 사용하는 것이 좋다.

 

ObjectId로 변환할 수 없는 type을 id로 지정한 경우 해당 속성이 MongoDB document의 _id field에 그대로 저장된다.

이 경우 해당 속성이 그대로 저장되기 때문에 저장 시 중복되지 않은 값을 매번 지정해야 한다.

또한 _id를 client에서 만들기 때문에 @GeneratedValue(strategy = GenerationType.IDENTITY)와 같이 Data JPA에서 사용하던 증가 값 설정을 할 수 없으며 개별 구현해야 한다.

결국 Data JPA처럼 identity를 구현하여 사용하는 것은 복잡도가 높고 불편한 개발 형태가 될 수밖에 없다.

(id를 매번 지정해서 사용하는 경우 @CreatedDate 같은 auditing metadata도 동작하지 않았다. - 왜 그런지는 확인하지 않았음)

 

또한 Data JPA는 @Id가 필수지만 data mongdb는 내부에 _id를 사용한다는 전제 조건이 있기 때문에 암시적으로 생성하여 사용할 수 있기 때문에 필수가 아니라는 차이가 있다.

id라는 이름의 field가 java domain에 없으면 암시적인 _id 가 생성되지만 java domain의 field에 매핑되지 않는다.

 

아래처럼 ObjectId나 BigInteger, String으로 id를 선언하여 사용하면 된다.

@Data
public class Notification {
	
	@Id
	private ObjectId id;
	
	private String message;

	// ... 이하 생략

}

ObjectId에서 위에 설명한 정보를  획득할 수 있다.

ObjectId id = notification.getId();
Date date = id.getDate();
int machineIdentifier = id.getMachineIdentifier();
short processIdentifier = id.getProcessIdentifier();
int counter = id.getCounter();

MongodDB가 _id에 ObjectId를 사용하므로 java domain도 ObjectId를 쓰는 것이 가장 좋다.

하지만 String이나 BigInteger를 사용할 수 있으며 선택하기 나름이다.

 

Repository 구현하기

MongoDB의 _id라는 default 속성을 가지고 있기 때문에 Repository를 만들 때 ID 값 설정이 Data JPA와 조금 다르다.

Data JPA의 경우 @Id가 선언된 field의 속성에 따라 아래처럼 Repository의 ID 속성을 선언해야 한다.

@Data
public class TestDomain {
    @Id
    private String id;
    // .. 이하 생략
}

// domain의 @Id field 속성과 동일하게 String으로 선언해야함
public interface TestDomainRepository extends CrudRepository<TestDomain, String> { 
}

반면 MongoDb는 domain @Id field 속성에 종속되지 않고 개별적으로 repository의 ID 선언을 설정할 수 있다.

@Data
public class TestDomain {
    @Id
    private ObjectId id;
    // .. 이하 생략
}

// domain @Id의 field 속성이 ObjectId여도 String으로 선언가능
public interface TestDomainRepository extends CrudRepository<TestDomain, String> { 
}

String, BigInteger, ObjectId는 셋 다 MongoDB에서 ObjectId 속성을 맵핑한다.

따라서 이 경우 repository의 ID선언을 ObjectId, String, BigInteger와 같이 사용할 수 있다. 

(그 외 유형의 ID type 선언도 runtime exception이 발생하진 않지만 해당 타입으로 호출할 _id 값이 일치하지 않아 null 값이 반환되었다.)

java domain의 id field는 ObjectId로 선언하면 _id의 속성 값을 사용하기 편하지만 repository ID 에선 findById(ID id)와 같이 호출을 하기 때문에 ObjectId나 BigInteger보단 String이 더 편한 듯하다.

(ObjectId나 BigInteger로 ID를 선언하면 해당 객체를 생성해서 사용해야 하는데 굳이 그렇게 사용할 필요가 있을까 싶다.)

Mono testDomainMono = testDomainRepository.findById("5c2037e1bc93944278291fb0");

 

Domain 간 관계 설정

Mongodb는 @OneToMany와 같은 형태의 선언이 필요 없다.

RDB table <-> java object 간 데이터 맵핑을 위한 설정이 필요 없는 이유는 하위 관계까지 java object 형태 그대로 Collection에 저장할 수 있기 때문이다.

다만 Collection에 저장할지 혹은 개별 Collection을 참조할지에 대해 선언을 해주는 @DBRef annotation을 지원한다.

반응형
profile

파란하늘의 지식창고

@Bluesky_

도움이 되었다면 광고를 클릭해주세요