Spring Boot에서 MongoDB 인덱스 자동증가 (autoincrement) 기능 구현하기

mongoDB는 id를 String 형태의 문자열로 구성하고 있고, 이를 강제로 Long으로 바꾸어 사용할 수 있지만, 그럴 경우 MongoRepository의 findBy 관련 함수들을 많이 튜닝해줘야 하는 단점이 보였기에 별도로 Long 형태의 idx를 collection에 추가하여 사용하려 합니다. RDS에서 제공하는 auto increment기능이 noSQL에는 없기 때문에, 각 collection의 idx 마지막 값을 저장하는 별도의 collection을 구성하여 auto increment 기능을 구현하였습니다.


1. collection들의 마지막 idx를 가지고 있는 DatabaseSequence Class 생성하기

  • members collection의 Long idx의 마지막 값을 가지는 database_sequences라는 명칭의 새로운 collection을 만들어서  사용해야 하므로 모델 개념의 클래스를 생성합니다. 
ackage com.kimmingyu.aws.model;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "database_sequences")
public class DatabaseSequence {

    @Id
    private String id;

    private long seq;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public long getSeq() {
        return seq;
    }

    public void setSeq(long seq) {
        this.seq = seq;
    }
}

 

2. members Model에 SEQENCE_NAME 선언 및 Long idx 추가

  • members 모델 안에 @Transient 어노테이션을 사용하여 SEQUENCE_NAME을 지정합니다.
  • Long 형태의 idx도 추가합니다.
  • users에서 사용할 거면 users_sequence, boards에서 사용할 거면 boards_sequence 등 일관성 있게 명명
ackage com.kimmingyu.aws.model;

import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;

@Document(collection = "members")
public class Member {
    @Transient
    public static final String SEQUENCE_NAME = "members_sequence";

    @Id
    private String id;
    private Long idx;
    @NotBlank
    @Size(max = 10)
    private String name;
    @NotBlank
    @Size(max = 100)
    @Indexed(unique = true)
    private String email;
    @NotBlank
    @Size(min = 10)
    private String password;
    private String phone;
    private String regDate;
    // getter & setter ...
}

 

3. Auto Increment 기능을 담당하는 SequenceGeneratorService 클래스 생성하기

  • 해당 collection에 대한 idx값이 없으면 1부터 idx를 시작하고
  • 해당 collection에 대한 idx값이 있으면 다음 숫자로 업데이트하여 자동 증가 기능을 담당해주는 서비스 클래스 생성합니다.
package com.kimmingyu.aws.service.classes;

import com.kimmingyu.aws.model.DatabaseSequence;
import static org.springframework.data.mongodb.core.FindAndModifyOptions.options;
import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.springframework.data.mongodb.core.query.Query.query;

import java.util.Objects;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

@Service
public class SequenceGeneratorService {

    private MongoOperations mongoOperations;

    @Autowired
    public SequenceGeneratorService(MongoOperations mongoOperations) {
        this.mongoOperations = mongoOperations;
    }

    public long generateSequence(String seqName) {

        DatabaseSequence counter = mongoOperations.findAndModify(query(where("_id").is(seqName)),
                new Update().inc("seq",1), options().returnNew(true).upsert(true),
                DatabaseSequence.class);
        return !Objects.isNull(counter) ? counter.getSeq() : 1;

    }
}

 

4. Member 관련 Controller에서 위의 구현체들을 사용하여 저장하기

// Member Class

@PostMapping(value = "/save", produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<?> saveOrUpdateMember(@RequestBody MemberDTO memberDTO) {

// sequenceGeneratorService를 사용하여 setIdx 해주기
memberDTO.setIdx(sequenceGeneratorService.generateSequence(Member.SEQUENCE_NAME));
// Update
memberService.saveOrUpdateMember(ObjectMapperUtils.map(memberDTO, Member.class));

}

 

5. 확인하기