Ćwiczenie — programowe tworzenie, odczytywanie, aktualizowanie i usuwanie danych NoSQL

Ukończone

Połączenie z usługą Azure Cosmos DB zostało nawiązane. W tej lekcji utworzysz dokumenty użytkowników w kolekcji WebCustomers. Następnie pobierzesz je według identyfikatora, zastąpisz i usuniesz.

Programistyczna praca z dokumentami

Dane są przechowywane w dokumentach JSON w usłudze Azure Cosmos DB. Dokumenty mogą być tworzone, pobierane, zastępowane lub usuwane w portalu lub programowo. To laboratorium koncentruje się na operacjach programowych. Usługa Azure Cosmos DB udostępnia zestawy SDK po stronie klienta dla oprogramowania .NET, .NET Core, Java, Node.js i Python, z których każdy obsługuje te operacje. W tym module będziemy używać zestawu SDK Java do wykonywania operacji CRUD (tworzenie, pobieranie, aktualizowanie i usuwanie) na danych NoSQL przechowywanych w usłudze Azure Cosmos DB.

Główne operacje dla dokumentów usługi Azure Cosmos DB to część klasy CosmosAsyncContainer:

Operacja upsert wykonuje operację tworzenia lub zastępowania w zależności od tego, czy dokument już istnieje.

Aby wykonać dowolną z tych operacji, potrzebne będą klasy pomocy (klasy POJO języka Java) reprezentujące obiekty przechowywane w bazie danych. Ponieważ pracujemy z bazą danych użytkowników, warto mieć klasę Userreprezentującą jednostki użytkowników. Będzie ona przechowywać podstawowe dane, takie jak imię, nazwisko i identyfikator użytkownika. (Identyfikator jest potrzebny, ponieważ jest kluczem partycji umożliwiającym skalowanie w poziomie).

Każdy użytkownik ma przypisane preferencje dostawy i kupony rabatowe, więc potrzebne będą typy danych ShippingPreference i CouponsUsed reprezentujące te jednostki. Każdy użytkownik może również mieć potencjalnie nieograniczoną historię zamówień, więc potrzebne będą oddzielne jednostki OrderHistory z odpowiadającą klasą POJO języka Java.

Przejdź do ścieżki src/main/java/com/azure/azure-cosmos-java-sql-app-mslearn i otwórz folder datatypes. Znajdziesz w nim kilka klas POJO:User, ShippingPreference, OrderHistory oraz CouponsUsed. Udostępniliśmy Ci wszystkie potrzebne klasy POJO jednostek i ich klasy pomocy.

Następnie utworzymy parę jednostek i wykonamy kilka podstawowych operacji CRUD na kontenerze usługi Azure Cosmos DB i dokumentach, które zawiera. Usłudze Azure Cosmos DB możesz przekazać wystąpienie ObjectNode biblioteki Jackson, która bezpośrednio określa dokument JSON. Jednak usługa Azure Cosmos DB umożliwia również serializację klas POJO języka Java do formatu JSON i zalecamy takie podejście jako najprostszą opcję (o ile pozostałe parametry są takie same).

Tworzenie dokumentów

  1. Otwórz plik User.java i przejrzyj jego zawartość. Powinna to wyglądać następująco:

    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;
    }
    

    Zwróć uwagę, że metody dostępu dla pól id, userId i innych są niejawne (niezdefiniowane w kodzie). Takie zachowanie jest możliwe, ponieważ używamy adnotacji @Data biblioteki Project Lombok w celu ich automatycznego tworzenia.

    Adnotacja @NoArgsConstructor wygeneruje konstruktor bez argumentów, który ustawia domyślne wartości pól. Adnotacja @AllArgsConstructor wygeneruje kolejny konstruktor z pełnym zestawem argumentów, który bezpośrednio określi wartości wszystkich pól.

    Zauważ, że klasa User ma właściwość id. Wszystkie dokumenty usługi Azure Cosmos DB wymagają właściwości id, dlatego wszystkie klasy POJO, które zamierzamy serializować do dokumentów JSON, muszą mieć pole id.

  2. Dodaj następującą metodę do pliku 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. Następnie wróć do metody basicOperations i dodaj następujący element na końcu tej metody przed wywołaniem metody 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. Skompiluj i uruchom plik CosmosApp.java w środowisku IDE lub uruchom program w terminalu przy użyciu polecenia:

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

    W miarę tego, jak aplikacja będzie tworzyć poszczególne nowe dokumenty użytkowników, w terminalu będą wyświetlane dane wyjściowe.

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

    Rejestrator może wyświetlić dodatkowy tekst, na przykład znaczniki czasu.

Gratulacje! Udało Ci się utworzyć pierwsze dane w usłudze Azure Cosmos DB z aplikacji języka Java. Zatrzymajmy się i oceńmy, co zostało zrobione.

W metodzie basicOperations są trzy nowe akcje:

  1. Utworzenie wystąpienia maxaxam klasy User.
  2. Utworzenie wystąpienia nelapin klasy User.
  3. Wywołanie metody createUserDocumentsIfNotExist z przekazaniem listy wystąpień maxaxam oraz nelapin.

Wywołanie metody createUserDocumentsIfNotExist powoduje wstawienie obu wystąpień klasy User jako elementów/dokumentów w usłudze Azure Cosmos DB. W przypadku przekazania wystąpień klasy User jako listy, naszym zamiarem jest modelowanie wydajnej metody do szybkiego pozyskiwania klas POJO w usłudze Azure Cosmos DB przy użyciu minimalnych zasobów obliczeniowych. Metoda createUserDocumentsIfNotExist implementuje wydajne asynchroniczne wstawianie listy klas POJO.

Załóżmy, że naszym celem jest maksymalizacja liczby żądań na sekundę na wątek. Dla porównania podejście synchroniczne do zapisu metody createUserDocumentsIfNotExist — ignorując przez chwilę kontrolę operacji readItem — polegałoby na wykonaniu iteracji dla każdego wystąpienia klasy User na liście users. Dla każdego wystąpienia klasy User u należałoby wykonać wywołanie blokujące operacji createItem:

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

Ten styl synchroniczny implementuje intuicyjny proces — wysłanie żądania, poczekanie na odpowiedź, wysłanie następnego żądania itd. Metoda createUserDocumentsIfNotExist nie stosuje jednak tego podejścia, ponieważ blokowanie wywołań będzie zasadniczo utratą cykli procesora CPU w trakcie czasu odpowiedzi na żądanie, co spowoduje niską liczbę żądań na sekundę.

Aby uniknąć tego problemu związanego z niską liczbą żądań na sekundę, możesz zduplikować kilka wątków, by umożliwić wykonywanie równoległych wywoływań żądań blokowania. Zduplikowane wątki skrócą czas wykonywania żądania. Jednak jeśli chcesz zaoszczędzić zasoby wątków, takie rozwiązanie również będzie nieekonomiczne. Każdy wątek wykonuję pętlę w trakcie czasu odpowiedzi na żądanie, gdy zamiast tego mógłby wykonywać wiele innych zadań jednocześnie, dając niską ilość żądań na sekundę na wątek.

Z tego powodu, oraz w celu pokazania wydajnego wątkowo wstawiania klas POJO języka Java, udostępniliśmy asynchroniczny przykład wstawiania dokumentu. Obsługa asynchroniczna zestawu Java SDK 4 usługi Azure Cosmos DB pochodzi z Project Reactor, struktury aplikacji języka Java, która zapewnia oparty na strumieniu model programowania deklaratywnego przepływu danych umożliwiający programowanie oparte na zdarzeniach asynchronicznych. Metoda createDocumentsIfNotExist implementuje programowanie asynchroniczne biblioteki Project Reactor.

W metodzie createUserDocumentsIfNotExist Flux.fromIterable(users) jest metodą tworzącą biblioteki Project Reactor. Tworzy wystąpienie klasy Flux, które jest źródłem zdarzeń asynchronicznych. W tym przypadku każde „zdarzenie” asynchroniczne zawiera argument wystąpienia klasy User. Wystąpienie klasy Flux zawiera dwa takie zdarzenia — jedno dla wystąpienia maxaxam i jedno dla wystąpienia nelapin. Kod metody .flatMap( ... ).blockLast(); definiuje potok operacji sekwencyjnych, które należy wykonać na zdarzeniach emitowanych przez wystąpienie klasy Flux.

Jedną z tych operacji jest createItem. Ten potok jest niemal identyczny do modelu synchronicznego, z tą różnicą, że nie wywołanie operacji createItem nie jest blokujące. Wywołanie metody blockLast() powoduje subskrypcję przygotowanego potoku, przez co wystąpienie Flux asynchronicznie emituje oba swoje zdarzenia. Następnie potok wewnątrz elementu .flatMap( ... ).blockLast(); przetwarza każde z tych zdarzeń w sposób pseudo-równoległy. Gdy jedno żądanie jest wystawiane i czeka na odpowiedź, biblioteka Project Reactor będzie przetwarzać inne żądania w tle, co jest krytycznym czynnikiem w maksymalizacji liczby żądań na sekundę na wątek.

Skoro poznaliśmy już wydajne asynchroniczne żądania bazy danych za pomocą biblioteki Project Reactor, dla uproszczenia w pozostałej części tego laboratorium będziemy używać wywołań blokujących (synchronicznych). Aby dowiedzieć się więcej na temat biblioteki Project Reactor, zobacz Przewodnik po wzorcu biblioteki Reactor usługi Azure Cosmos DB.

Czytanie dokumentów

  1. W celu odczytu dokumentów z bazy danych, dodaj następującą metodę do klasy 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. Skopiuj i wklej następujący kod na końcu metody basicOperations po kodzie tworzenia dokumentu:

    readUserDocument(maxaxam);
    
  3. Skompiluj i uruchom plik CosmosApp.java w środowisku IDE lub uruchom program w terminalu przy użyciu polecenia:

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

    Terminal wyświetli następujący komunikat, przy czym tekst „Read user 1” („Odczyt — użytkownik 1”) wskazuje, że dokument został pobrany.

    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
    

    Rejestrator może też wyświetlić dodatkowy tekst.

Zastępowanie dokumentów

Usługa Azure Cosmos DB obsługuje zastępowanie dokumentów JSON. W tym przypadku zaktualizujemy rekord użytkownika, aby uwzględnić zmianę jego nazwiska.

  1. Dodaj metodę replaceUserDocument po metodzie readUserDocument w pliku CosmosApp.java.

    /**
     * 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. Skopiuj i wklej następujący kod na końcu basicOperations metody po przeczytaniu kodu dokumentu.

    maxaxam.setLastName("Suh");
    replaceUserDocument(maxaxam);
    
  3. Skompiluj i uruchom plik CosmosApp.java w środowisku IDE lub uruchom program w terminalu przy użyciu polecenia:

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

    Terminal wyświetli następujący komunikat, przy czym tekst „Replaced last name for Suh” („Zmieniono nazwisko użytkownika Suh”) wskazuje, że dokument został zastąpiony.

    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
    

Usuwanie dokumentów

  1. Skopiuj i wklej metodę deleteUserDocument pod metodą replaceUserDocument.

    /**
     * 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. Skopiuj i wklej następujący kod na końcu metody basicOperations.

    deleteUserDocument(maxaxam);
    
  3. Skompiluj i uruchom plik CosmosApp.java w środowisku IDE lub uruchom program w terminalu przy użyciu polecenia:

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

    Terminal wyświetli następujący komunikat, przy czym tekst „Deleted user 1” („Usunięto — użytkownik 1”) wskazuje, że dokument został usunięty.

    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
    

Programistyczna praca z dokumentami

Dane są przechowywane w dokumentach JSON w usłudze Azure Cosmos DB. Dokumenty mogą być tworzone, pobierane, zastępowane lub usuwane w portalu lub programowo. To laboratorium koncentruje się na operacjach programowych. Wszystkie te operacje są dostępne w zestawie JAVA SDK usługi Azure Cosmos DB i są również dostępne za pośrednictwem modelu programowania Spring Data. W tym module będziemy używać projektu Spring Data w usłudze Azure Cosmos DB do wykonywania operacji CRUD (tworzenie, pobieranie, aktualizowanie i usuwanie) na danych NoSQL przechowywanych w usłudze Azure Cosmos DB.

Główne operacje dotyczące dokumentów projektu Spring Data w usłudze Azure Cosmos DB to podstawowe operacje w modelu programowania Spring Data:

  • save — pisanie lub aktualizowanie dokumentu, w zależności od tego, czy dokument już istnieje.
  • view — odczytywanie dokumentu
  • delete — usuwanie dokumentu

Aby wykonać dowolną z tych operacji, potrzebne będą klasy pomocy (klasy POJO języka Java) reprezentujące obiekty przechowywane w bazie danych. Ponieważ pracujemy z bazą danych klientów online, warto użyć klasy WebCustomer do reprezentowania jednostek użytkowników. Będzie ona przechowywać podstawowe dane, takie jak imię, nazwisko i identyfikator użytkownika. (Identyfikator jest potrzebny, ponieważ jest kluczem partycji umożliwiającym skalowanie w poziomie).

Każdy użytkownik w Internecie ma przypisane preferencje dostawy i kupony rabatowe, więc potrzebne będą typy danych ShippingPreference i CouponsUsed reprezentujące te jednostki. Każdy użytkownik w Internecie może również mieć potencjalnie nieograniczoną historię zamówień, więc potrzebne będą oddzielne jednostki OrderHistory z odpowiadającą klasą POJO języka Java.

Przejdź do folderu src/main/java/com/azure/cosmos/examples/springexamples. Zobaczysz klasę POJO WebCustomer. Teraz przejdź do folderu common. Znajdziesz w nim kilka klas POJO: ShippingPreference, OrderHistory i CouponsUsed. Udostępniliśmy Ci wszystkie potrzebne klasy POJO jednostek i ich klasy pomocy.

Następnie utworzymy parę jednostek i wykonamy kilka podstawowych operacji CRUD na kontenerze usługi Azure Cosmos DB i dokumentach, które zawiera. Usłudze Azure Cosmos DB możesz przekazać wystąpienie ObjectNode biblioteki Jackson, która bezpośrednio określa dokument JSON. Jednak usługa Azure Cosmos DB umożliwia również serializację klas POJO języka Java do formatu JSON i zalecamy takie podejście jako najprostszą opcję (o ile pozostałe parametry są takie same).

Tworzenie i aktualizowanie dokumentów

  1. Otwórz plik WebCustomer.java i przejrzyj jego zawartość. Powinna to wyglądać następująco:

    // 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;
    }
    

    Zwróć uwagę, że metody dostępu dla pól id, userId i innych są niejawne (niezdefiniowane w kodzie). Takie zachowanie jest możliwe, ponieważ używamy adnotacji @Data biblioteki Project Lombok w celu ich automatycznego tworzenia.

    Adnotacja @NoArgsConstructor wygeneruje konstruktor bez argumentów, który ustawia domyślne wartości pól. Adnotacja @AllArgsConstructor wygeneruje kolejny konstruktor z pełnym zestawem argumentów, który bezpośrednio określi wartości wszystkich pól.

    Zauważ, że klasa WebCustomer ma właściwość id. Wszystkie dokumenty usługi Azure Cosmos DB wymagają właściwości id, dlatego wszystkie klasy POJO, które zamierzamy serializować do dokumentów JSON, muszą mieć pole id.

  2. Dodaj następującą metodę do pliku 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. Wróć do metody run i dodaj następujący kod na końcu tej metody.

    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. Skompiluj i uruchom plik CosmosSample.java w środowisku IDE lub uruchom program w terminalu przy użyciu polecenia:

    mvn clean package
    mvn spring-boot:run
    

    Oprócz danych wyjściowych terminalu powinny zostać wyświetlone następujące dane

    INFO: Creating WebCustomer 1
    INFO: Creating WebCustomer 2
    

Gratulacje! Udało Ci się utworzyć i/lub zaktualizować pierwsze dane w usłudze Azure Cosmos DB na podstawie aplikacji języka Java. Zatrzymajmy się i oceńmy, co zostało zrobione.

W metodzie run są trzy nowe akcje:

  1. Utworzenie/zaktualizowanie wystąpienia maxaxam klasy WebCustomer.
  2. Utworzenie/zaktualizowanie wystąpienia nelapin klasy WebCustomer.
  3. Wywołanie metody createWebCustomerDocumentsIfNotExist z przekazaniem listy wystąpień maxaxam oraz nelapin.

Wywołanie metody createWebCustomerDocumentsIfNotExist powoduje wstawienie obu wystąpień klasy WebCustomer jako elementów/dokumentów w usłudze Azure Cosmos DB. W przypadku przekazania wystąpień klasy WebCustomer jako listy, naszym zamiarem jest modelowanie wydajnej metody do szybkiego pozyskiwania klas POJO w usłudze Azure Cosmos DB przy użyciu minimalnych zasobów obliczeniowych. Metoda createWebCustomerDocumentsIfNotExist implementuje wydajne asynchroniczne wstawianie listy klas POJO. Jeśli dowolny dokument już istnieje, element save wykona aktualizację zamiast tworzenia dokumentu.

Załóżmy, że naszym celem jest maksymalizacja liczby żądań na sekundę na wątek. Dla porównania podejście synchroniczne do zapisu metody createWebCustomerDocumentsIfNotExist polegałoby na wykonaniu iteracji dla każdego wystąpienia klasy WebCustomer w elemencie webCustomers. Dla każdego wystąpienia klasy WebCustomer webCustomer należałoby wykonać wywołanie blokujące operacji save:

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

Ten styl synchroniczny implementuje intuicyjny proces — wysłanie żądania, poczekanie na odpowiedź, wysłanie następnego żądania itd. Metoda createWebCustomerDocumentsIfNotExist nie stosuje jednak tego podejścia, ponieważ blokowanie wywołań będzie zasadniczo utratą cykli procesora CPU w trakcie czasu odpowiedzi na żądanie, co spowoduje niską liczbę żądań na sekundę.

Aby uniknąć tego problemu związanego z niską liczbą żądań na sekundę, możesz zduplikować kilka wątków, by umożliwić wykonywanie równoległych wywoływań żądań blokowania. Zduplikowane wątki skrócą czas wykonywania żądania. Jednak jeśli chcesz zaoszczędzić zasoby wątków, takie rozwiązanie również będzie nieekonomiczne. Każdy wątek wykonuję pętlę w trakcie czasu odpowiedzi na żądanie, gdy zamiast tego mógłby wykonywać wiele innych zadań jednocześnie, dając niską ilość żądań na sekundę na wątek.

Z tego powodu, oraz w celu pokazania wydajnego wątkowo wstawiania klas POJO języka Java, udostępniliśmy asynchroniczny przykład wstawiania dokumentu. Obsługa asynchroniczna projektu Spring Data pochodzi z rozwiązania Project Reactor, struktury aplikacji języka Java, która zapewnia oparty na strumieniu model programowania deklaratywnego przepływu danych umożliwiający programowanie oparte na zdarzeniach asynchronicznych. Metoda createWebCustomerDocumentsIfNotExist implementuje programowanie asynchroniczne biblioteki Project Reactor.

W metodzie createWebCustomerDocumentsIfNotExist Flux.fromIterable(webCustomers) jest metodą tworzącą biblioteki Project Reactor. Tworzy wystąpienie klasy Flux, które jest źródłem zdarzeń asynchronicznych. W tym przypadku każde „zdarzenie” asynchroniczne zawiera argument wystąpienia klasy WebCustomer. Wystąpienie klasy Flux zawiera dwa takie zdarzenia — jedno dla wystąpienia maxaxam i jedno dla wystąpienia nelapin. Kod metody .flatMap( ... ).blockLast(); definiuje potok operacji sekwencyjnych, które należy wykonać na zdarzeniach emitowanych przez wystąpienie klasy Flux.

W takim przypadku dwie operacje w potoku są wywołaniami elementu save. Ten potok jest niemal identyczny do modelu synchronicznego, z tą różnicą, że nie wywołanie operacji save nie jest blokujące. Wywołanie metody blockLast() powoduje subskrypcję przygotowanego potoku, przez co wystąpienie Flux asynchronicznie emituje oba swoje zdarzenia. Następnie potok wewnątrz elementu .flatMap( ... ).blockLast(); przetwarza każde z tych zdarzeń w sposób pseudo-równoległy. Gdy jedno żądanie jest wystawiane i czeka na odpowiedź, biblioteka Project Reactor będzie przetwarzać inne żądania w tle, co jest krytycznym czynnikiem w maksymalizacji liczby żądań na sekundę na wątek.

Skoro poznaliśmy już wydajne asynchroniczne żądania bazy danych za pomocą biblioteki Project Reactor, dla uproszczenia w pozostałej części tego laboratorium będziemy używać wywołań blokujących asynchronicznych (a w rzeczywistości wywołań synchronicznych). Aby dowiedzieć się więcej na temat biblioteki Project Reactor, zobacz Przewodnik po wzorcu biblioteki Reactor usługi Azure Cosmos DB.

Czytanie dokumentów

  1. W celu odczytu dokumentów z bazy danych, dodaj następującą metodę do klasy 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. Skopiuj i wklej następujący kod na końcu metody run po kodzie tworzenia dokumentu:

    readWebCustomerDocument(maxaxam);
    
  3. Skompiluj i uruchom plik CosmosSample.java w środowisku IDE lub uruchom program w terminalu przy użyciu polecenia:

    mvn clean package
    mvn spring-boot:run
    

    Oprócz danych wyjściowych terminalu powinny zostać wyświetlone poniższe dane. „Read user 1” (Odczyt — użytkownik 1) oznacza, że dokument został pobrany.

    INFO: Read webCustomer 1
    

Usuwanie dokumentów

  1. Skopiuj i wklej metodę deleteWebCustomerDocument pod metodą readWebCustomerDocument.

    /**
     * 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. Skopiuj i wklej następujący kod na końcu metody run.

    deleteWebCustomerDocument(maxaxam);
    
  3. Skompiluj i uruchom plik CosmosSample.java w środowisku IDE lub uruchom program w terminalu przy użyciu polecenia:

    mvn clean package
    mvn spring-boot:run
    

    Oprócz danych wyjściowych terminalu powinny zostać wyświetlone poniższe dane. „Deleted user 1” (Usunięto — użytkownik 1) oznacza, że dokument został usunięty.

    INFO: Deleted webCustomer 1
    

W tej lekcji utworzyliśmy, zaktualizowaliśmy, odczytaliśmy i usunęliśmy dokumenty w bazie danych usługi Azure Cosmos DB.