1. 개요

기본적으로 MongoDB Java 드라이버는 ObjectId 유형의 ID를 생성합니다 . 때로는 UUID 와 같은 객체의 고유 식별자로 다른 유형의 데이터를 사용하고자 할 수 있습니다 . 그러나 MongoDB Java 드라이버는 UUID를 자동으로 생성할 수 없습니다 .

이 예제에서는 MongoDB Java 드라이버와 Spring Data MongoDB 를 사용하여 UUID를 생성하는 세 가지 방법을 살펴보겠습니다 .

2. 공통점

애플리케이션이 한 가지 유형의 데이터만 관리하는 경우는 매우 드뭅니다. MongoDB 데이터베이스에서 ID 관리를 단순화하려면 모든 문서 클래스의 ID를 정의하는 추상 클래스를 구현하는 것이 더 쉽습니다.

public abstract class UuidIdentifiedEntity {

    @Id   
    protected UUID id;    

    public void setId(UUID id) {

        if (this.id != null) {
            throw new UnsupportedOperationException("ID is already defined");
        }

        this.id = id;
    }

    // Getter
}

이 사용방법(예제)의 예제에서는 모든 클래스가 이 클래스에서 상속된 MongoDB 데이터베이스에 유지된다고 가정합니다.

3. UUID 지원 구성

MongoDB에서 UUID 저장을 허용하려면 드라이버를 구성해야 합니다. 이 구성은 매우 간단하며 드라이버에게 데이터베이스에 UUID를 저장하는 방법만 알려줍니다. 여러 응용 프로그램이 동일한 데이터베이스를 사용하는 경우 이를 신중하게 처리해야 합니다.

시작 시 MongoDB 클라이언트에서 uuidRepresentation 매개변수 를 지정하기 만 하면 됩니다 .

@Bean
public MongoClient mongo() throws Exception {
    ConnectionString connectionString = new ConnectionString("mongodb://localhost:27017/test");
    MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
      .uuidRepresentation(UuidRepresentation.STANDARD)
      .applyConnectionString(connectionString).build();
    return MongoClients.create(mongoClientSettings);
}

Spring Boot를 사용하는 경우 application.properties 파일에서 이 매개변수를 지정할 수 있습니다.

spring.data.mongodb.uuid-representation=standard

4. 수명 주기 이벤트 사용

UUID 생성을 처리하는 첫 번째 방법은 Spring의 수명 주기 이벤트를 사용하는 것입니다. MongoDB 엔터티를 사용하면 JPA 어노테이션 @PrePersist 등을 사용할 수 없습니다. 따라서 ApplicationContext 에 등록된 이벤트 리스너 클래스를 구현해야 합니다 . 이를 위해 클래스는 Spring의 AbstractMongoEventListener 클래스 를 확장해야 합니다 .

public class UuidIdentifiedEntityEventListener extends AbstractMongoEventListener<UuidIdentifiedEntity> {
    
    @Override
    public void onBeforeConvert(BeforeConvertEvent<UuidIdentifiedEntity> event) {
        
        super.onBeforeConvert(event);
        UuidIdentifiedEntity entity = event.getSource();
        
        if (entity.getId() == null) {
            entity.setId(UUID.randomUUID());
        } 
    }    
}

이 경우 Spring이 Java 개체를 Document 개체로 변환하고 MongoDB 드라이버로 보내기 전에 트리거되는 OnBeforeConvert 이벤트를 사용하고 있습니다.

UuidIdentifiedEntity 클래스 를 잡기 위해 이벤트를 입력 하면 이 추상 상위 유형의 모든 하위 클래스를 처리할 수 있습니다. Spring은 UUID를 ID로 사용하는 객체가 변환되는 즉시 코드를 호출합니다.

우리는 Spring 이 비동기일 수 있는 TaskExecutor 에 이벤트 처리를 위임한다는 것을 알아야 합니다 . Spring은 객체가 효과적으로 변환되기 전에 이벤트가 처리된다는 것을 보장하지 않습니다. 개체가 변환된 후 ID가 생성되어 예외가 발생할 수 있으므로 TaskExecutor 가 비동기 인 경우 이 메서드는 사용하지 않는 것이 좋습니다 .

InvalidDataAccessApiUsageException: 엔티티에 대해 java.util.UUID 유형의 ID를 자동 생성할 수 없습니다.

@Component 로 어노테이션을 달거나 @Configuration 클래스 에서 생성 하여 ApplicationContext 에 이벤트 리스너를 등록할 수 있습니다 .

@Bean
public UuidIdentifiedEntityEventListener uuidIdentifiedEntityEventListener() {
    return new UuidIdentifiedEntityEventListener();
}

5. 엔티티 콜백 사용

Spring 인프라는 엔터티 수명 주기의 일부 지점에서 사용자 지정 코드를 실행하기 위한 후크를 제공합니다. 이를 EntityCallbacks 라고 하며 객체가 데이터베이스에 유지되기 전에 UUID를 생성하는 데 사용할 수 있습니다.

이전에 본 이벤트 리스너 메서드와 달리 콜백은 실행이 동기식 이며 코드가 개체 수명 주기의 예상 지점에서 실행되도록 보장합니다 .

Spring Data MongoDB는 애플리케이션에서 사용할 수 있는 일련의 콜백을 제공합니다. 우리의 경우 이전과 동일한 이벤트를 사용합니다. 콜백은 @Configuration 클래스 에서 직접 제공될 수 있습니다 .

@Bean
public BeforeConvertCallback<UuidIdentifiedEntity> beforeSaveCallback() {
        
    return (entity, collection) -> {
          
        if (entity.getId() == null) {
            entity.setId(UUID.randomUUID());
        }
        return entity;
    };
}

BeforeConvertCallback 인터페이스 를 구현 하는 구성 요소 를 사용할 수도 있습니다 .

6. 사용자 지정 저장소 사용

Spring Data MongoDB는 우리의 목표를 달성하기 위한 세 번째 방법인 사용자 지정 저장소 구현을 사용하는 방법을 제공합니다. 일반적으로 MongoRepository를 상속받은 인터페이스를 선언하면 Spring이 저장소 관련 코드를 처리합니다.

Spring Data가 객체를 처리하는 방식을 변경하려는 경우 Spring이 저장소 수준에서 실행할 사용자 지정 코드를 정의할 수 있습니다. 이렇게 하려면 먼저 MongoRepository 를 확장하는 인터페이스를 정의해야 합니다 .

@NoRepositoryBean
public interface CustomMongoRepository<T extends UuidIdentifiedEntity> extends MongoRepository<T, UUID> { }

@NoRepositoryBean 어노테이션은 Spring 이 MongoRepository 와 관련된 일반적인 코드 조각을 생성하는 것을 방지합니다 . 이 인터페이스는 객체의 ID 유형으로 UUID를 강제로 사용합니다.

그런 다음 UUID를 처리하는 데 필요한 동작을 정의할 저장소 클래스를 만들어야 합니다.

public class CustomMongoRepositoryImpl<T extends UuidIdentifiedEntity> 
  extends SimpleMongoRepository<T, UUID> implements CustomMongoRepository<T>

이 리포지토리에서 SimpleMongoRepository 의 관련 메서드 를 재정의하여 ID를 생성해야 하는 모든 메서드 호출을 잡아야 합니다 . 이러한 방법은 우리의 경우 save()insert() 입니다.

@Override
public <S extends T> S save(S entity) {
    generateId(entity);
    return super.save(entity);
}

마지막으로 기본 구현 대신 리포지토리 구현으로 사용자 정의 클래스를 사용하도록 Spring에 지시해야 합니다. @Configuration 클래스 에서 이를 수행합니다 .

@EnableMongoRepositories(basePackages = "com.baeldung.repository", repositoryBaseClass = CustomMongoRepositoryImpl.class)

그런 다음 평소와 같이 변경 없이 리포지토리를 선언할 수 있습니다.

public interface BookRepository extends MongoRepository<Book, UUID> { }

7. 결론

이 기사에서는 Spring Data MongoDB를 사용하여 UUID를 MongoDB 객체의 ID로 구현하는 세 가지 방법을 살펴보았습니다.

항상 그렇듯이 이 기사에서 사용된 코드는 GitHub 에서 사용할 수 있습니다 .

Persistence footer banner