연습 - 프로그래밍 방식으로 NoSQL 데이터 만들기, 읽기, 업데이트 및 삭제

완료됨

Azure Cosmos DB에 연결했습니다. 이 단원에서는 WebCustomers 컬렉션에 사용자 문서를 만듭니다. 그런 다음, 해당 문서를 ID별로 검색하고, 바꾸고, 삭제합니다.

프로그래밍 방식으로 문서 작업

데이터는 Azure Cosmos DB의 JSON 문서에 저장됩니다. 문서는 포털에서 또는 프로그래밍 방식으로 만들거나, 검색하거나, 바꾸거나, 삭제할 수 있습니다. 이 랩에서는 프로그래밍 방식 작업을 중점적으로 살펴봅니다. Azure Cosmos DB는 .NET, .NET Core, Java, Node.js 및 Python에 사용되는 클라이언트 쪽 SDK를 제공합니다. 이러한 도구는 각각 해당 작업을 지원합니다. 이 모듈에서는 Java SDK를 사용하여 Azure Cosmos DB에 저장된 NoSQL 데이터에 대해 CRUD(만들기, 검색, 업데이트 및 삭제) 작업을 수행합니다.

Azure Cosmos DB 문서에 대한 기본 작업은 CosmosAsyncContainer 클래스의 일부입니다.

Upsert는 문서가 이미 존재하는지 여부에 따라 만들기 또는 바꾸기 작업을 수행합니다.

이러한 작업을 수행하려면 데이터베이스에 저장된 개체를 나타내는 도우미 클래스(Java POJO 클래스)가 필요합니다. 사용자로 구성된 데이터베이스로 작업하고 있으므로 사용자 엔터티를 나타내는 User 클래스가 필요합니다. 이 클래스는 이름, 성, 사용자 ID와 같은 기본 데이터를 저장합니다. (수평 스케일링을 사용하기 위한 파티션 키이므로 ID가 필요합니다.)

사용자마다 관련된 배송 기본 설정 및 쿠폰이 있기 때문에 ShippingPreferenceCouponsUsed 데이터 형식을 사용하여 해당 엔터티를 나타낼 수 있습니다. 마지막으로, 각 사용자는 제한 없는 주문 기록을 가지고 있을 수 있으므로 별도의 OrderHistory 엔터티와 이에 대응되는 Java POJO 클래스도 필요합니다.

src/main/java/com/azure/azure-cosmos-java-sql-app-mslearn에서 datatypes 폴더를 확인합니다. User, ShippingPreference, OrderHistory, CouponsUsed 등 여러 POJO가 표시됩니다. 이렇게 모든 엔터티 POJO와 도우미 클래스를 제공해 드렸습니다.

다음으로, 몇몇 엔터티를 만들고 Azure Cosmos DB 컨테이너와 이 컨테이너에 포함된 문서에 대해 기본적인 CRUD 작업을 수행해 보겠습니다. JSON 문서를 직접 지정하는 Jackson ObjectNode 인스턴스를 Azure Cosmos DB에 전달할 수 있습니다. 그러나 Azure Cosmos DB는 Java POJO를 JSON으로 직렬화할 수 있으므로, 다른 모든 조건이 동일하다면 가장 간단한 방법인 이 방법을 권장합니다.

문서 만들기

  1. User.java를 열고 내용을 살펴봅니다. 다음과 같이 표시됩니다.

    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    import java.util.List;
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User {
    
        /** Document ID (required by Azure Cosmos DB). */
        private String id;
    
        /** User ID. */
        private String userId;
    
        /** User last name. */
        private String lastName;
    
        /** User first name. */
        private String firstName;
    
        /** User email address. */
        private String email;
    
        /** User dividend setting. */
        private String dividend;
    
        /** User shipping preferences. */
        private ShippingPreference shippingPreference;
    
        /** User order history. */
        private List<OrderHistory> orderHistory;
    
        /** Coupons recorded by the user. */
        private List<CouponsUsed> coupons;
    }
    

    id, userId와 같은 필드의 액세스 메서드가 암시적(코드에 정의되어 있지 않음)임을 알 수 있습니다. 이는 Project Lombok @Data 주석을 사용하여 필드를 자동으로 만들었기 때문에 가능한 것입니다.

    @NoArgsConstructor 주석은 기본 필드 값을 설정하는 인수 없이 생성자를 생성합니다. @AllArgsConstructor 주석은 모든 필드 값을 직접 지정하기 위해 전체 인수 집합을 사용하여 다른 생성자를 생성합니다.

    User에는 id 속성이 있습니다. 모든 Azure Cosmos DB 문서에는 id 속성이 필요합니다. 따라서 JSON 문서로 직렬화하려는 모든 POJO는 id 필드를 가져야 합니다.

  2. CosmosApp.java에 다음 메서드를 추가합니다.

    /**
     * Take in list of Java POJOs, check if each exists, and if not insert it.
     * @param users List of User POJOs to insert.
     */
    private static void createUserDocumentsIfNotExist(final List<User> users) {
        Flux.fromIterable(users).flatMap(user -> {
            try {
                container.readItem(user.getId(), new PartitionKey(user.getUserId()), User.class).block();
                logger.info("User {} already exists in the database", user.getId());
                return Mono.empty();
            } catch (Exception err) {
                logger.info("Creating User {}", user.getId());
                return container.createItem(user, new PartitionKey(user.getUserId()), new CosmosItemRequestOptions());
            }
        }).blockLast();
    }
    
  3. basicOperations 메서드로 돌아가서 client.close() 호출 직전에 메서드의 끝부분에서 다음 코드를 추가합니다.

    User maxaxam = new User(
        "1",
        "maxaxam",
        "Axam",
        "Max",
        "maxaxam@contoso.com",
        "2.0",
        new ShippingPreference(
            1,
            "90 W 8th St",
            "",
            "New York",
            "NY",
            "10001",
            "USA"
        ),
        new ArrayList<OrderHistory>(Arrays.asList(
            new OrderHistory(
                "3",
                "1000",
                "08/17/2018",
                "52.49"
            )
        )),
        new ArrayList<CouponsUsed>(Arrays.asList(
            new CouponsUsed(
                "A7B89F"
            )
        ))
    );
    
    User nelapin = new User(
            "2",
            "nelapin",
            "Pindakova",
            "Nela",
            "nelapin@contoso.com",
            "8.50",
            new ShippingPreference(
                1,
                "505 NW 5th St",
                "",
                "New York",
                "NY",
                "10001",
                "USA"
            ),
            new ArrayList<OrderHistory>(Arrays.asList(
                new OrderHistory(
                    "4",
                    "1001",
                    "08/17/2018",
                    "105.89"
                )
            )),
            new ArrayList<CouponsUsed>(Arrays.asList(
                new CouponsUsed(
                    "Fall 2018"
                )
            ))
    );
    
    createUserDocumentsIfNotExist(new ArrayList<User>(Arrays.asList(maxaxam, nelapin)));
    
  4. IDE에서 CosmosApp.java를 빌드하고 실행하거나 터미널에서 다음을 사용하여 프로그램을 실행합니다.

    mvn clean package
    mvn exec:java -Dexec.mainClass="com.azure.cosmos.examples.mslearnbasicapp.CosmosApp"
    

    애플리케이션이 새 사용자 문서를 만들 때마다 터미널에 출력이 표시됩니다.

    INFO: Database and container validation complete
    INFO: Creating User 1
    INFO: Creating User 2
    

    로거에서 타임스탬프와 같은 그 밖의 추가 텍스트도 출력할 수 있습니다.

축하합니다! Java 애플리케이션에서 Azure Cosmos DB에 첫 번째 데이터를 만들었습니다. 여기서 잠깐 멈추고 지금까지 한 작업을 살펴보겠습니다.

basicOperations에는 다음과 같은 세 가지 작업이 있습니다.

  1. maxaxam User 인스턴스를 만듭니다.
  2. nelapin User 인스턴스를 만듭니다.
  3. createUserDocumentsIfNotExist를 호출하여 목록에 있는 maxaxamnelapin을 전달합니다.

createUserDocumentsIfNotExist를 호출하면 두 User 인스턴스가 모두 Azure Cosmos DB에 항목/문서로 삽입됩니다. User 인스턴스를 목록으로 전달하도록 한 이유는 컴퓨팅 리소스를 최소한으로 사용하면서 Azure Cosmos DB에 POJO를 빠르게 주입하는 우수한 성능의 메서드를 모델링하기 위함이었습니다. createUserDocumentsIfNotExist는 POJO 목록의 효율적인 비동기 삽입을 구현합니다.

우리의 목표가 스레드 하나당 초당 요청을 최대화하는 것이라고 가정해 보겠습니다. 비교를 위해 살펴보면 여기에서 readItem 검사는 잠시 무시하고, createUserDocumentsIfNotExist를 쓰는 비동기식 방법은 users의 각 User 인스턴스에 대해 반복을 수행합니다. 각 User u에 대해 createItem에 대한 블록화 호출을 실행합니다.

container.createItem(u, new PartitionKey(u.getUserId()), new CosmosItemRequestOptions()).block(); // <= Note the .block() which loops until request response.

이 동기식 스타일은 요청 발급, 응답 대기, 다음 요청 발급이라는 직관적인 프로세스를 구현합니다. 그러나 createUserDocumentsIfNotExist는 이 방법을 사용하지 않습니다. 블록화 호출은 요청 응답 시간 중에 CPU 주기를 낭비하여 초당 요청이 낮아지는 원인이 되기 때문입니다.

여러 개의 스레드를 생성하여 블록화 요청 호출을 병렬로 처리하면 이 초당 요청 문제를 해결하는 것이 가능할 수 있습니다. 여러 스레드를 사용하면 실행 시간이 향상됩니다. 하지만 목표가 스레드 리소스를 아껴 쓰는 것이라면, 이 방법에서도 낭비가 발생합니다. 각 스레드는 원래라면 다른 작업을 멀티태스킹할 수 있는 요청 응답 시간에 반복되므로 스레드 하나당 초당 요청이 낮아집니다.

이러한 이유에 더해 여러분에게 Java POJO의 스레드 효율적인 삽입을 보여 주기 위해, 동기식 방법 대신 문서 삽입의 비동기 예제를 제시했습니다. Azure Cosmos DB Java SDK v4의 비동기 지원은 비동기 이벤트 기반 프로그래밍을 위한 스트림 기반 선언적 데이터 흐름 프로그래밍 모델을 제공하는 Java 애플리케이션 프레임워크인 Project Reactor에서 옵니다. createDocumentsIfNotExist는 Project Reactor 비동기 프로그래밍을 구현합니다.

createUserDocumentsIfNotExist에서 Flux.fromIterable(users)은 Project Reactor 팩터리 메서드입니다. 비동기 이벤트의 원본인 Flux 인스턴스를 만듭니다. 이 경우 각 비동기 "이벤트"에는 User 인스턴스 인수가 포함됩니다. Flux 인스턴스에는 두 개의 이벤트가 포함되어 있으며 하나는 maxaxam, 다른 하나는 nelapin 이벤트입니다. .flatMap( ... ).blockLast(); 내의 코드는 Flux 인스턴스에서 내보낸 이벤트에 대해 수행할 순차적 작업의 파이프라인을 정의합니다.

이러한 작업 중 하나가 createItem입니다. createItem 호출 시 차단하지 않는다는 점을 제외하고 이 파이프라인은 동기 구현과 거의 동일합니다. 특히 어셈블된 파이프라인에 대한 blockLast() 구독 호출은 Flux가 두 이벤트를 비동기식으로 전송하도록 합니다. 그런 다음 .flatMap( ... ).blockLast(); 내부의 파이프라인이 의사 병렬 방식으로 각 이벤트를 처리합니다. 하나의 요청이 발행되어 응답을 대기할 때, Project Reactor는 백그라운드에서 다른 요청을 처리하는데, 이는 스레드 하나당 초당 요청을 최대화하기 위한 중요한 요인이 됩니다.

Project Reactor로 효율적인 비동기 데이터베이스 요청의 예를 살펴보았으니, 이 랩의 나머지 부분에서는 편의상 블록화(동기식) 호출을 사용하겠습니다. Project Reactor에 대해 자세히 알아보려면 Azure Cosmos DB Reactor Pattern Guide(Reactor 패턴 가이드)를 참조하세요.

문서 읽기

  1. 데이터베이스에서 문서를 읽으려면 CosmosApp에 다음 메서드를 추가합니다.

    /**
     * Take in a Java POJO argument, extract ID and partition key, and read the corresponding document from the container.
     * In this case the ID is the partition key.
     * @param user User POJO to pull ID and partition key from.
     */
    private static CosmosItemResponse<User> readUserDocument(final User user) {
        CosmosItemResponse<User> userReadResponse = null;
    
        try {
            userReadResponse = container.readItem(user.getId(), new PartitionKey(user.getUserId()), User.class).block();
            logger.info("Read user {}", user.getId());
        } catch (CosmosException de) {
            logger.error("Failed to read user {}", user.getId(), de);
        }
    
        return userReadResponse;
    }
    
  2. 다음 코드를 복사하여 basicOperations 메서드의 끝부분에서 문서 만들기 코드 뒤에 붙여넣습니다.

    readUserDocument(maxaxam);
    
  3. IDE에서 CosmosApp.java를 빌드하고 실행하거나 터미널에서 다음을 사용하여 프로그램을 실행합니다.

    mvn clean package
    mvn exec:java -Dexec.mainClass="com.azure.cosmos.examples.mslearnbasicapp.CosmosApp"
    

    터미널은 다음 출력을 표시합니다. 여기서 "사용자 1 읽음"은 문서가 검색되었음을 나타냅니다.

    INFO: Database and container validation complete
    INFO: User 1 already exists in the database
    INFO: User 2 already exists in the database
    INFO: Read user 1
    

    로거에서 그 밖의 추가 텍스트도 출력할 수 있습니다.

문서 바꾸기

Azure Cosmos DB는 JSON 문서 바꾸기를 지원합니다. 이 경우에 사용자 레코드를 업데이트하여 사용자의 성 변경을 처리합니다.

  1. CosmosApp.java 파일의 readUserDocument 메서드 뒤에 replaceUserDocument 메서드를 추가합니다.

    /**
     * Take in a Java POJO argument, extract ID and partition key,
     * and replace the existing document with the same ID and partition key to match.
     * @param user User POJO representing the document update.
     */
    private static void replaceUserDocument(final User user) {
        try {
            CosmosItemResponse<User> userReplaceResponse = container.replaceItem(user, user.getId(), new PartitionKey(user.getUserId())).block();
            logger.info("Replaced User {}", user.getId());
        } catch (CosmosException de) {
            logger.error("Failed to replace User {}", user.getUserId());
        }
    }
    
  2. 다음 코드를 복사하여 basicOperations 메서드의 끝부분에서 문서 읽기 코드 뒤에 붙여넣습니다.

    maxaxam.setLastName("Suh");
    replaceUserDocument(maxaxam);
    
  3. IDE에서 CosmosApp.java를 빌드하고 실행하거나 터미널에서 다음을 사용하여 프로그램을 실행합니다.

    mvn clean package
    mvn exec:java -Dexec.mainClass="com.azure.cosmos.examples.mslearnbasicapp.CosmosApp"
    

    터미널은 다음 출력을 표시합니다. 여기서, "Suh의 성을 바꿈"은 문서가 바뀌었음을 나타냅니다.

    INFO: Database and container validation complete
    INFO: User 1 already exists in the database
    INFO: User 2 already exists in the database
    INFO: Read user 1
    INFO: Replaced last name for Suh
    

문서 삭제

  1. replaceUserDocument 메서드 아래에서 deleteUserDocument 메서드를 복사해서 붙여넣습니다.

    /**
     * Take in a Java POJO argument, extract ID and partition key,
     * and delete the corresponding document.
     * @param user User POJO representing the document update.
     */
    private static void deleteUserDocument(final User user) {
        try {
            container.deleteItem(user.getId(), new PartitionKey(user.getUserId())).block();
            logger.info("Deleted user {}", user.getId());
        } catch (CosmosException de) {
            logger.error("User {} could not be deleted.", user.getId());
        }
    }
    
  2. basicOperations 메서드 끝에 다음 코드를 복사해서 붙여넣습니다.

    deleteUserDocument(maxaxam);
    
  3. IDE에서 CosmosApp.java를 빌드하고 실행하거나 터미널에서 다음을 사용하여 프로그램을 실행합니다.

    mvn clean package
    mvn exec:java -Dexec.mainClass="com.azure.cosmos.examples.mslearnbasicapp.CosmosApp"
    

    터미널은 다음 출력을 표시합니다. 여기서 "사용자 1 삭제됨"은 문서가 삭제되었음을 나타냅니다.

    INFO: Database and container validation complete
    INFO: User 1 already exists in the database
    INFO: User 2 already exists in the database
    INFO: Read User 1
    INFO: Replaced last name for Suh
    INFO: Deleted User 1
    

프로그래밍 방식으로 문서 작업

데이터는 Azure Cosmos DB의 JSON 문서에 저장됩니다. 문서는 포털에서 또는 프로그래밍 방식으로 만들거나, 검색하거나, 바꾸거나, 삭제할 수 있습니다. 이 랩에서는 프로그래밍 방식 작업을 중점적으로 살펴봅니다. 이 모든 작업은 Azure Cosmos DB Java SDK에서 사용 가능하고 Spring Data 프로그래밍 모델을 통해 액세스할 수 있습니다. 이 모듈에서는 Spring Data Azure Cosmos DB를 사용하여 Azure Cosmos DB에 저장된 NoSQL 데이터에 대해 CRUD(만들기, 검색, 업데이트 및 삭제) 작업을 수행합니다.

Spring Data Azure Cosmos DB 문서에 대한 기본 작업은 Spring Data 프로그래밍 모델의 기본 작업입니다.

  • save - 문서가 이미 존재하는지 여부에 따라 문서를 지점 쓰기 또는 업데이트합니다.
  • view - 문서를 지점 읽기합니다.
  • delete - 문서를 지점 삭제합니다.

이러한 작업을 수행하려면 데이터베이스에 저장된 개체를 나타내는 도우미 클래스(Java POJO 클래스)가 필요합니다. 온라인 고객으로 구성된 데이터베이스로 작업하고 있으므로 사용자 엔터티를 나타내는 WebCustomer 클래스를 사용하고자 할 것입니다. 이 클래스는 이름, 성, 사용자 ID와 같은 기본 데이터를 저장합니다. (수평 스케일링을 사용하기 위한 파티션 키이므로 ID가 필요합니다.)

웹 고객마다 관련된 배송 기본 설정 및 쿠폰이 있기 때문에 ShippingPreferenceCouponsUsed 데이터 형식을 사용하여 해당 엔터티를 나타낼 수 있습니다. 마지막으로, 각 웹 고객은 제한 없는 주문 기록을 가지고 있을 수 있으므로 별도의 OrderHistory 엔터티와 이에 대응되는 Java POJO 클래스도 필요합니다.

src/main/java/com/azure/cosmos/examples/springexamples로 이동합니다. WebCustomer POJO가 표시됩니다. 이제 common 폴더를 살펴봅니다. ShippingPreference, OrderHistory, CouponsUsed 등 여러 POJO가 표시됩니다. 이렇게 모든 엔터티 POJO와 도우미 클래스를 제공해 드렸습니다.

다음으로, 몇몇 엔터티를 만들고 Azure Cosmos DB 컨테이너와 이 컨테이너에 포함된 문서에 대해 기본적인 CRUD 작업을 수행해 보겠습니다. JSON 문서를 직접 지정하는 Jackson ObjectNode 인스턴스를 Azure Cosmos DB에 전달할 수 있습니다. 그러나 Azure Cosmos DB는 Java POJO를 JSON으로 직렬화할 수 있으므로, 다른 모든 조건이 동일하다면 가장 간단한 방법인 이 방법을 권장합니다.

문서 만들기 및 업데이트

  1. WebCustomer.java를 열고 내용을 살펴봅니다. 다음과 같이 표시됩니다.

    // Copyright (c) Microsoft Corporation. All rights reserved.
    // Licensed under the MIT License.
    package com.azure.cosmos.examples.springexamples;
    
    import com.azure.cosmos.examples.springexamples.common.CouponsUsed;
    import com.azure.cosmos.examples.springexamples.common.OrderHistory;
    import com.azure.cosmos.examples.springexamples.common.ShippingPreference;
    import com.azure.spring.data.cosmos.core.mapping.Container;
    import com.azure.spring.data.cosmos.core.mapping.PartitionKey;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    import java.util.List;
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @Container(containerName = "WebCustomer", ru = "400")
    public class WebCustomer {
    
        /** Document ID (required by Azure Cosmos DB). */
        private String id;
    
        /** WebCustomer ID. */
        private String userId;
    
        /** WebCustomer last name. */
        @PartitionKey
        private String lastName;
    
        /** WebCustomer first name. */
        private String firstName;
    
        /** WebCustomer email address. */
        private String email;
    
        /** WebCustomer dividend setting. */
        private String dividend;
    
        /** WebCustomer shipping preferences. */
        private ShippingPreference shippingPreference;
    
        /** WebCustomer order history. */
        private List<OrderHistory> orderHistory;
    
        /** Coupons recorded by the user. */
        private List<CouponsUsed> coupons;
    }
    

    id, userId와 같은 필드의 액세스 메서드가 암시적(코드에 정의되어 있지 않음)임을 알 수 있습니다. 이는 Project Lombok @Data 주석을 사용하여 필드를 자동으로 만들었기 때문에 가능한 것입니다.

    @NoArgsConstructor 주석은 기본 필드 값을 설정하는 인수 없이 생성자를 생성합니다. @AllArgsConstructor 주석은 모든 필드 값을 직접 지정하기 위해 전체 인수 집합을 사용하여 다른 생성자를 생성합니다.

    WebCustomer에는 id 속성이 있습니다. 모든 Azure Cosmos DB 문서에는 id 속성이 필요합니다. 따라서 JSON 문서로 직렬화하려는 모든 POJO는 id 필드를 가져야 합니다.

  2. CosmosSample.java에 다음 메서드를 추가합니다.

    /**
     * Take in list of Java POJOs and insert them into the database.
     * @param webCustomers List of WebCustomer POJOs to insert.
     */
    private void createWebCustomerDocumentsIfNotExist(final List<WebCustomer> webCustomers) {
        Flux.fromIterable(webCustomers).flatMap(webCustomer -> {
            logger.info("Creating WebCustomer {}", webCustomer.getId());
            return this.reactiveWebCustomerRepository.save(webCustomer);
        }).blockLast();
    }
    
  3. run 메서드를 찾고 다음 코드를 메서드의 끝에 추가합니다.

    WebCustomer maxaxam = new WebCustomer(
            "1",
            "maxaxam",
            "Axam",
            "Max",
            "maxaxam@contoso.com",
            "2.0",
            new ShippingPreference(
                    1,
                    "90 W 8th St",
                    "",
                    "New York",
                    "NY",
                    "10001",
                    "USA"
            ),
            new ArrayList<OrderHistory>(Arrays.asList(
                    new OrderHistory(
                            "3",
                            "1000",
                            "08/17/2018",
                            "52.49"
                    )
            )),
            new ArrayList<CouponsUsed>(Arrays.asList(
                    new CouponsUsed(
                            "A7B89F"
                    )
            ))
    );
    
    WebCustomer nelapin = new WebCustomer(
            "2",
            "nelapin",
            "Pindakova",
            "Nela",
            "nelapin@contoso.com",
            "8.50",
            new ShippingPreference(
                    1,
                    "505 NW 5th St",
                    "",
                    "New York",
                    "NY",
                    "10001",
                    "USA"
            ),
            new ArrayList<OrderHistory>(Arrays.asList(
                    new OrderHistory(
                            "4",
                            "1001",
                            "08/17/2018",
                            "105.89"
                    )
            )),
            new ArrayList<CouponsUsed>(Arrays.asList(
                    new CouponsUsed(
                            "Fall 2018"
                    )
            ))
    );
    
    createWebCustomerDocumentsIfNotExist(new ArrayList(Arrays.asList(maxaxam, nelapin)));
    
  4. IDE에서 CosmosSample.java를 빌드하고 실행하거나 터미널에서 다음을 사용하여 프로그램을 실행합니다.

    mvn clean package
    mvn spring-boot:run
    

    터미널 출력 가운데 다음이 표시됩니다.

    INFO: Creating WebCustomer 1
    INFO: Creating WebCustomer 2
    

축하합니다! Java 애플리케이션에서 Azure Cosmos DB에 첫 번째 데이터를 만들고 업데이트했습니다. 여기서 잠깐 멈추고 지금까지 한 작업을 살펴보겠습니다.

run에는 다음과 같은 세 가지 작업이 있습니다.

  1. maxaxam WebCustomer 인스턴스를 만들고 업데이트합니다.
  2. nelapin WebCustomer 인스턴스를 만들고 업데이트합니다.
  3. createWebCustomerDocumentsIfNotExist를 호출하여 목록에 있는 maxaxamnelapin을 전달합니다.

createWebCustomerDocumentsIfNotExist를 호출하면 두 WebCustomer 인스턴스가 모두 Azure Cosmos DB에 항목/문서로 삽입됩니다. WebCustomer 인스턴스를 목록으로 전달하도록 한 이유는 컴퓨팅 리소스를 최소한으로 사용하면서 Azure Cosmos DB에 POJO를 빠르게 주입하는 우수한 성능의 메서드를 모델링하기 위함이었습니다. createWebCustomerDocumentsIfNotExist는 POJO 목록의 효율적인 비동기 삽입을 구현합니다. 문서가 이미 존재하는 경우 save에서 문서를 만드는 대신 업데이트를 수행합니다.

우리의 목표가 스레드 하나당 초당 요청을 최대화하는 것이라고 가정해 보겠습니다. 비교를 위해 살펴보면 createWebCustomerDocumentsIfNotExist를 쓰는 비동기식 방법은 webCustomers의 각 WebCustomer 인스턴스에 대해 반복을 수행합니다. 각 WebCustomer webCustomer에 대해 save에 대한 블록화 호출을 실행합니다.

this.reactiveWebCustomerRepository.save(webCustomer).block(); // <= Note the .block() which loops until request response.

이 동기식 스타일은 요청 발급, 응답 대기, 다음 요청 발급이라는 직관적인 프로세스를 구현합니다. 그러나 createWebCustomerDocumentsIfNotExist는 이 방법을 사용하지 않습니다. 블록화 호출은 요청 응답 시간 중에 CPU 주기를 낭비하여 초당 요청이 낮아지는 원인이 되기 때문입니다.

여러 개의 스레드를 생성하여 블록화 요청 호출을 병렬로 처리하면 이 초당 요청 문제를 해결하는 것이 가능할 수 있습니다. 여러 스레드를 사용하면 실행 시간이 향상됩니다. 하지만 목표가 스레드 리소스를 아껴 쓰는 것이라면, 이 방법에서도 낭비가 발생합니다. 각 스레드는 원래라면 다른 작업을 멀티태스킹할 수 있는 요청 응답 시간에 반복되므로 스레드 하나당 초당 요청이 낮아집니다.

이러한 이유에 더해 여러분에게 Java POJO의 스레드 효율적인 삽입을 보여 주기 위해, 동기식 방법 대신 문서 삽입의 비동기 예제를 제시했습니다. Spring Data의 비동기 지원은 비동기 이벤트 기반 프로그래밍을 위한 스트림 기반 선언적 데이터 흐름 프로그래밍 모델을 제공하는 Java 애플리케이션 프레임워크인 Project Reactor에서 옵니다. createWebCustomerDocumentsIfNotExist는 Project Reactor 비동기 프로그래밍을 구현합니다.

createWebCustomerDocumentsIfNotExist에서 Flux.fromIterable(webCustomers)은 Project Reactor 팩터리 메서드입니다. 비동기 이벤트의 원본인 Flux 인스턴스를 만듭니다. 이 경우 각 비동기 "이벤트"에는 WebCustomer 인스턴스 인수가 포함됩니다. Flux 인스턴스에는 두 개의 이벤트가 포함되어 있으며 하나는 maxaxam, 다른 하나는 nelapin 이벤트입니다. .flatMap( ... ).blockLast(); 내의 코드는 Flux 인스턴스에서 내보낸 이벤트에 대해 수행할 순차적 작업의 파이프라인을 정의합니다.

이 경우 파이프라인의 두 작업은 save 호출입니다. save 호출 시 차단하지 않는다는 점을 제외하고 이 파이프라인은 동기 구현과 거의 동일합니다. 특히 어셈블된 파이프라인에 대한 blockLast() 구독 호출은 Flux가 두 이벤트를 비동기식으로 전송하도록 합니다. 그런 다음 .flatMap( ... ).blockLast(); 내부의 파이프라인이 의사 병렬 방식으로 각 이벤트를 처리합니다. 하나의 요청이 발행되어 응답을 대기할 때, Project Reactor는 백그라운드에서 다른 요청을 처리하는데, 이는 스레드 하나당 초당 요청을 최대화하기 위한 중요한 요인이 됩니다.

Project Reactor로 효율적인 비동기 데이터베이스 요청의 예를 살펴보았으니, 이 랩의 나머지 부분에서는 블록화 비동기 호출(실질적으로 동기식 호출)을 사용하겠습니다. Project Reactor에 대해 자세히 알아보려면 Azure Cosmos DB Reactor Pattern Guide(Reactor 패턴 가이드)를 참조하세요.

문서 읽기

  1. 데이터베이스에서 문서를 읽으려면 CosmosSample에 다음 메서드를 추가합니다.

    /**
     * Take in a Java POJO argument, extract ID and partition key, and read the corresponding document from the container.
     * In this case the ID is the partition key.
     * @param webCustomer User POJO to pull ID and partition key from.
     */
    private WebCustomer readWebCustomerDocument(final WebCustomer webCustomer) {
        WebCustomer webCustomerResult = null;
    
        try {
            logger.info("Read webCustomer {}", webCustomer.getId());
            webCustomerResult = this.reactiveWebCustomerRepository.findById(webCustomer.getId(), new PartitionKey(webCustomer.getLastName())).block();
        } catch (CosmosException de) {
            logger.error("Failed to read webCustomer {}", webCustomer.getId(), de);
        }
    
        return webCustomer;
    }
    
  2. 다음 코드를 복사하여 run 메서드의 끝부분에서 문서 만들기 코드 뒤에 붙여넣습니다.

    readWebCustomerDocument(maxaxam);
    
  3. IDE에서 CosmosSample.java를 빌드하고 실행하거나 터미널에서 다음을 사용하여 프로그램을 실행합니다.

    mvn clean package
    mvn spring-boot:run
    

    터미널 출력 가운데 다음이 표시됩니다. "사용자 1 읽음"은 문서가 검색되었음을 나타냅니다.

    INFO: Read webCustomer 1
    

문서 삭제

  1. readWebCustomerDocument 메서드 아래에서 deleteWebCustomerDocument 메서드를 복사해서 붙여넣습니다.

    /**
     * Take in a Java POJO argument, extract ID and partition key,
     * and delete the corresponding document.
     * @param webCustomer User POJO representing the document update.
     */
    private void deleteWebCustomerDocument(final WebCustomer webCustomer) {
        try {
            this.reactiveWebCustomerRepository.deleteById(webCustomer.getId(),new PartitionKey(webCustomer.getLastName())).block();
            logger.info("Deleted webCustomer {}", webCustomer.getId());
        } catch (CosmosException de) {
            logger.error("User {} could not be deleted.", webCustomer.getId());
        }
    }
    
  2. run 메서드 끝에 다음 코드를 복사해서 붙여넣습니다.

    deleteWebCustomerDocument(maxaxam);
    
  3. IDE에서 CosmosSample.java를 빌드하고 실행하거나 터미널에서 다음을 사용하여 프로그램을 실행합니다.

    mvn clean package
    mvn spring-boot:run
    

    터미널 출력 가운데 다음이 표시됩니다. "사용자 1 삭제함"은 문서가 삭제되었음을 나타냅니다.

    INFO: Deleted webCustomer 1
    

이 단원에서는 Azure Cosmos DB 데이터베이스에서 문서를 만들고, 업데이트하고, 읽고, 삭제했습니다.