개요
이 가이드 에서는 Java Reactive Streams 운전자 에서 BSON 데이터에 대한 Java 객체의 인코딩 및 디코딩을 처리하다 코덱 및 지원 클래스에 대해 학습 수 있습니다.Codec 추상화를 사용하면 모든 Java 유형을 해당 BSON types에 매핑할 수 있습니다.Document 또는 와 같은 중간 맵 기반 객체 사용하는 대신 이 추상화를 사용하여 도메인 객체를 BSON 에 직접 매핑할 수 BsonDocument 있습니다.
다음 섹션에서 Codec 추상화를 사용하여 사용자 지정 인코딩 및 디코딩 로직을 지정하는 방법을 알아보세요.
일반 Java 객체(POJO)에 대한 인코딩 및 디코딩 로직을 사용자 지정하는 방법에 대해 학습 POJO를 사용하여 데이터 모델링 가이드 참조하세요.
코덱 구현
Codec 인터페이스에는 Java 객체를 BSON 데이터로 직렬화 및 역직렬화하기 위한 추상 메서드가 포함되어 있습니다. 이 인터페이스 구현 에서 BSON 과 Java 객체 간의 변환 로직을 정의합니다.
Codec 인터페이스를 구현하려면 encode(), decode() 및 getEncoderClass() 추상 메서드를 재정의합니다.
이 encode() 메서드에는 다음 매개 변수가 필요합니다.
Parameter Type | 설명 |
|---|---|
| BSON 문서 작성하기 위한 메서드를 노출하는 인터페이스 유형인 |
| 구현에서 인코딩하는 데이터입니다. 유형은 구현에 할당된 유형 변수와 일치해야 합니다. |
| 현재 값을 MongoDB 컬렉션 에 저장 할지 여부를 포함하여 BSON 으로 인코딩하는 Java 객체 데이터에 대한 메타데이터 포함되어 있습니다. |
이 메서드는 BsonWriter 인스턴스를 사용하여 인코딩된 값을 MongoDB에 보내고 값을 반환하지 않습니다.
decode() 메서드는 BSON 데이터의 값으로 채워진 Java 인스턴스 객체를 반환합니다. 이 메서드에는 다음 매개 변수가 필요합니다:
Parameter Type | 설명 |
|---|---|
| BSON 문서를 읽기 위한 메서드를 노출하는 인터페이스 유형인 |
| Java 객체로 디코딩되는 BSON 데이터에 대한 정보를 포함합니다. |
Java 유형 삭제로 인해 유형을 추론할 수 없으므로 getEncoderClass() 메서드는 Java 클래스의 클래스 인스턴스 반환합니다.
예시
다음 코드 예제는 사용자 지정 Codec를 구현 방법을 보여줍니다.
ReadStatus 열거형 책을 읽었는지 여부를 나타내는 READ 및 UNREAD 값이 포함되어 있습니다.
public enum ReadStatus { READ, UNREAD }
ReadStatusCodec 클래스는 Codec 를 구현하여 Java enum 값을 해당 BSON 부울 값으로 변환합니다. encode() 메서드는 ReadStatus 를 BSON 부울로 변환하고, decode() 메서드는 반대 방향으로 변환을 수행합니다.
public class ReadStatusCodec implements Codec<ReadStatus> { public void encode(BsonWriter writer, ReadStatus value, EncoderContext encoderContext) { writer.writeBoolean(value.equals(ReadStatus.READ) ? Boolean.TRUE : Boolean.FALSE); } public ReadStatus decode(BsonReader reader, DecoderContext decoderContext) { return reader.readBoolean() ? ReadStatus.READ : ReadStatus.UNREAD; } public Class<ReadStatus> getEncoderClass() { return ReadStatus.class; } }
의 인스턴스 에 추가할 수 있으며, 여기에는 와 해당 인스턴스가 적용되는 Java 객체 유형 ReadStatusCodec CodecRegistry간의 매핑이 포함되어 Codec 있습니다. 이 페이지의 CodecRegistry 섹션으로 계속 이동하여 를 포함하는 방법을 Codec 확인합니다.
이 섹션의 클래스 및 인터페이스에 대한 자세한 내용은 다음 API 설명서를 참조하세요.
CodecRegistry 사용
CodecRegistry는 지정된 Java 클래스를 인코딩하고 디코딩하는 변경이 불가능한 Codec 인스턴스 컬렉션입니다. 다음과 같은 CodecRegistries 클래스의 정적 팩토리 메서드 중 하나를 사용하여 연결된 유형에 포함된 Codec 인스턴스로부터 CodecRegistry를 구성할 수 있습니다:
fromCodecs()fromProviders()fromRegistries()
다음 코드 스니펫은 fromCodecs() 메서드를 사용하여 CodecRegistry 를 구성하는 방법을 보여줍니다.
CodecRegistry codecRegistry = CodecRegistries.fromCodecs(new IntegerCodec(), new ReadStatusCodec());
앞의 예시 에서 CodecRegistry 에는 다음과 같은 Codec 구현이 포함되어 있습니다.
IntegerCodecIntegers를 변환하며, BSON 패키지의 일부를 구성하는Codec입니다.ReadStatusCodec,
CodecJava 열거형 값을 BSON 부울로 변환하는 샘플.
다음 코드를 사용하여 앞의 예시 의 CodecRegistry 인스턴스 에서 Codec 인스턴스를 조회 할 수 있습니다.
Codec<ReadStatus> readStatusCodec = codecRegistry.get(ReadStatus.class); Codec<Integer> integerCodec = codecRegistry.get(Integer.class);
등록되지 않은 클래스에 대한 Codec 인스턴스 조회 하려고 하면 get() 메서드에서 CodecConfigurationException가 발생합니다.
이 섹션의 클래스 및 인터페이스에 대한 자세한 내용은 다음 API 설명서를 참조하세요.
CodecProvider 사용
CodecProvider는 Codec 인스턴스를 생성하여 CodecRegistry 인스턴스에 할당하는 추상 메서드를 포함하는 인터페이스입니다. CodecRegistry와 유사하게, BSON 라이브러리는 get() 메서드로 검색된 Codec 인스턴스를 사용하여 Java와 BSON 데이터 유형을 서로 변환합니다.
그러나 해당 Codec 객체가 필요한 필드가 포함된 클래스를 추가하는 경우 클래스의 Codec 를 인스턴스화하기 전에 클래스 필드에 대한 Codec 객체를 인스턴스화해야 합니다. get() 메서드의 CodecRegistry 매개변수를 사용하여 Codec 가 의존하는 Codec 인스턴스를 전달할 수 있습니다.
다음 코드 예시 인스턴스 CodecProvider 에 BookCodec Codec 필요한 CodecRegistry 인스턴스(예:ReadStatusCodec 앞 예시 의 )를 전달하기 위해 을 구현 방법을 보여줍니다.
public class BookCodecProvider implements CodecProvider { public BookCodecProvider() {} public <T> Codec<T> get(Class<T> clazz, CodecRegistry registry) { if (clazz == Book.class) { return (Codec<T>) new BookCodec(registry); } // return null when not a provider for the requested class return null; } }
이러한 Codec 클래스를 사용하여 읽기 및 쓰기 (write) 작업을 보여주는 실행 가능한 예시 보려면 이 가이드 의 사용자 지정 코덱 예제 섹션을 참조하세요.
POJO로 작업할 때는 PojoCodecProvider 일반적으로 사용되는 데이터 유형을 변환하고 해당 동작을 사용자 지정할 때 중복 코드를 최소화하기 위해 를 사용하는 것이 좋습니다. 자세한 내용은 POJO를 사용하여 데이터 모델링하기 가이드 참조하세요.
기본 코덱 레지스트리 사용
기본값 코덱 레지스트리는 일반적으로 사용되는 Java 과 MongoDB 유형 간의 변환을 지정하는 CodecProvider 클래스 설정하다 입니다. 다른 코덱 레지스트리를 지정하지 않는 한 운전자 자동으로 기본값 코덱 레지스트리를 사용합니다.
하나 이상의 Codec 클래스의 동작을 재정의하고 다른 클래스의 기본값 코덱 레지스트리의 동작을 유지해야 하는 경우 모든 레지스트리를 우선 순위에 따라 지정할 수 있습니다. 예시 들어 열거형 형 유형에 대한 Codec 의 기본값 제공자 동작을 사용자 지정 MyEnumCodec로 재정의하려면 다음 예시 와 같이 기본값 코덱 레지스트리 앞에 레지스트리 목록에 추가합니다.
CodecRegistry newRegistry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs(new MyEnumCodec()), MongoClientSettings.getDefaultCodecRegistry());
이 섹션의 클래스 및 인터페이스에 대한 자세한 내용은 다음 API 설명서 섹션을 참조하세요.
유형 매핑 사용자 지정
BsonTypeClassMap 클래스에는 BSON 과 Java 유형 간의 권장 매핑이 포함되어 있습니다. 사용자 지정 Codec 또는 CodecProvider 에서 이 클래스를 사용하면 Document 클래스와 같이 Iterable 또는 Map 를 구현 컨테이너 클래스로 BSON 유형을 디코딩하는 Java 유형을 관리 데 도움이 될 수 있습니다.
새 항목 또는 대체 항목이 포함된 Map 를 전달하여 BsonTypeClassMap 기본값 매핑을 추가하거나 수정할 수 있습니다.
다음 코드 스니펫은 기본값 BsonTypeClassMap 인스턴스 에서 BSON 유형에 해당하는 Java 클래스 유형을 조회 방법을 보여줍니다.
BsonTypeClassMap bsonTypeClassMap = new BsonTypeClassMap(); Class<?> clazz = bsonTypeClassMap.get(BsonType.ARRAY); System.out.println("Java type: " + clazz.getName());
이 코드는 다음을 출력합니다:
Java type: java.util.List
BsonTypeClassMap 생성자에 대체 항목을 지정하여 인스턴스 에서 이러한 매핑을 수정할 수 있습니다. 다음 예시 BsonTypeClassMap 인스턴스 의 ARRAY 에 대한 매핑을 Set 클래스로 바꾸는 방법을 보여 줍니다.
Map<BsonType, Class<?>> replacements = new HashMap<BsonType, Class<?>>(); replacements.put(BsonType.ARRAY, Set.class); BsonTypeClassMap bsonTypeClassMap = new BsonTypeClassMap(replacements); Class<?> clazz = bsonTypeClassMap.get(BsonType.ARRAY); System.out.println("Java type: " + clazz.getName());
이 코드는 다음을 출력합니다:
Java type: java.util.Set
기본값 매핑의 전체 목록은 BsonTypeClassMap API 설명서를 참조하세요.
팁
클래스가 Document 을 사용하는 방법에 대한 예는 BsonTypeClassMap DocumentCodecProvider 및 DocumentCodec 클래스의 운전자 소스 코드 참조하세요.
사용자 지정 코덱 예시
이 섹션에서는 Codec 및 CodecProvider 를 구현 사용자 지정 Java 클래스에 대한 인코딩 및 디코딩 로직을 정의하는 방법을 보여 줍니다. 또한 사용자 지정 구현을 지정하고 사용하여 삽입 및 조회 작업을 수행하는 방법도 보여줍니다.
다음 예시 Book 이라는 사용자 지정 클래스와 MongoDB 컬렉션 에서 저장 및 검색할 수 있는 해당 필드를 정의합니다.
public class Book { private String title; private ReadStatus readStatus = ReadStatus.UNREAD; private Integer pageCount; public Book() {} // ...
이 클래스에는 다음 필드가 포함되며 각 필드에는 Codec이(가) 필요합니다.
title에는 BSON 라이브러리에 포함된StringCodec을 ( 를) 사용하는String값이 포함되어 있습니다.readStatus책을 읽었는지 여부를 설명하며, 이 예시 에서는 열거형 값을 BSON 부울로 변환하는 사용자 지정 ReadStatusCodec을 사용합니다.pageCountBSON 라이브러리에 포함된IntegerCodec을 ( 를) 사용하는Integer값을 포함합니다.
다음 코드 예시 Book 클래스에 대해 Codec 를 구현 방법을 보여줍니다. 참고로, 생성자는 필드를 인코딩 및 디코딩하는 데 필요한 Codec 인스턴스를 검색하는 CodecRegistry 인스턴스 를 예상합니다.
public class BookCodec implements Codec<Book> { private Codec<String> stringCodec; private Codec<ReadStatus> readStatusCodec; private Codec<Integer> integerCodec; public BookCodec(CodecRegistry registry) { this.stringCodec = registry.get(String.class); this.readStatusCodec = registry.get(ReadStatus.class); this.integerCodec = registry.get(Integer.class); } // Defines an encode() method to convert Book field values to BSON values public void encode(BsonWriter writer, Book value, EncoderContext encoderContext) { writer.writeStartDocument(); writer.writeName("title"); stringCodec.encode(writer, value.getTitle(), encoderContext); writer.writeName("readStatus"); readStatusCodec.encode(writer, value.getReadStatus(), encoderContext); writer.writeName("pageCount"); integerCodec.encode(writer, value.getPageCount(), encoderContext); writer.writeEndDocument(); } // Defines a decode() method to convert BSON values to Book field values public Book decode(BsonReader reader, DecoderContext decoderContext) { Book book = new Book(); reader.readStartDocument(); while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) { String fieldName = reader.readName(); if (fieldName.equals("title")) { book.setTitle(stringCodec.decode(reader, decoderContext)); } else if (fieldName.equals("readStatus")) { book.setReadStatus(readStatusCodec.decode(reader, decoderContext)); } else if (fieldName.equals("pageCount")) { book.setPageCount(integerCodec.decode(reader, decoderContext)); } else if (fieldName.equals("_id")) { reader.readObjectId(); } else { reader.skipValue(); } } reader.readEndDocument(); return book; } // Returns an instance of the Book class, since Java cannot infer the class type public Class<Book> getEncoderClass() { return Book.class; } }
필드에 대한 Codec 인스턴스를 Book에 사용할 수 있도록 하려면 다음 코드 예시 에 표시된 사용자 지정 CodecProvider 를 구현 .
public class BookCodecProvider implements CodecProvider { public BookCodecProvider() {} public <T> Codec<T> get(Class<T> clazz, CodecRegistry registry) { if (clazz == Book.class) { return (Codec<T>) new BookCodec(registry); } // return null when not a provider for the requested class return null; } }
변환 로직을 정의한 후 다음 작업을 수행할 수 있습니다.
Book인스턴스의 데이터를 MongoDB에 저장MongoDB에서 인스턴스로 데이터 검색
Book
다음 예시 클래스에는 BookCodecProvider 를 withCodecRegistry() 메서드에 전달하여 MongoCollection 인스턴스 에 할당하는 코드가 포함되어 있습니다. 또한 예시 클래스는 Book 클래스 및 관련 코덱을 사용하여 데이터를 삽입하고 검색합니다.
public class BookCodecExample { public static void main(String[] args) { String uri = "<MongoDB connection URI>"; try (MongoClient mongoClient = MongoClients.create(uri)) { CodecRegistry codecRegistry = CodecRegistries.fromRegistries( CodecRegistries.fromCodecs(new ReadStatusCodec()), CodecRegistries.fromProviders(new BookCodecProvider()), MongoClientSettings.getDefaultCodecRegistry()); MongoDatabase database = mongoClient.getDatabase("codecs_example_db"); MongoCollection<Book> collection = database.getCollection("books", Book.class) .withCodecRegistry(codecRegistry); // construct and insert an instance of Book Book myBook = new Book(); myBook.setTitle("The Hobbit"); myBook.setReadStatus(ReadStatus.READ); myBook.setPageCount(310); Mono.from(collection.insertOne(myBook)).block(); // retrieve one or more instances of Book Flux.from(collection.find()) .doOnNext(System.out::println) .blockLast(); } } }
앞의 예시 실행 하면 출력은 다음과 유사합니다.
Book [title=The Hobbit, readStatus=READ, pageCount=310]
API 문서
이 가이드 에 언급된 메서드 및 클래스에 대한 자세한 내용은 다음 API 문서를 참조하세요.