Übung: Programmgesteuertes Erstellen, Lesen, Aktualisieren und Löschen von NoSQL-Daten

Abgeschlossen

Sie haben eine Verbindung mit Azure Cosmos DB hergestellt. In dieser Lerneinheit erstellen Sie Benutzerdokumente in Ihrer WebCustomers-Sammlung. Anschließend rufen Sie die Dokumente anhand der ID ab, ersetzen sie und löschen sie.

Programmgesteuertes Arbeiten mit Dokumenten

Daten werden in Azure Cosmos DB als JSON-Dokumente gespeichert. Dokumente können entweder im Portal erstellt, abgerufen, ersetzt oder gelöscht werden, oder programmgesteuert. In diesem Lab liegt der Fokus auf programmgesteuerten Vorgängen. Azure Cosmos DB bietet clientseitige SDKs für .NET, .NET Core, Java, Node.js und Python, die diese Vorgänge unterstützen. In diesem Modul wird das Java SDK verwendet, um CRUD-Vorgänge (Create, Retrieve, Update, Delete – Erstellen, Abrufen, Aktualisieren, Löschen) für die in Azure Cosmos DB gespeicherten NoSQL-Daten auszuführen.

Die wichtigsten Vorgänge für Azure Cosmos DB-Dokumente sind Teil der Klasse CosmosAsyncContainer:

Upsert führt abhängig davon, ob das Dokument bereits vorhanden ist, einen Vorgang zum Erstellen oder Ersetzen durch.

Sie benötigen Hilfsklassen (POJO-Klassen in Java), die die in der Datenbank gespeicherten Objekte darstellen, um diese Vorgänge durchführen zu können. Da Sie mit einer Benutzerdatenbank arbeiten, sollten Sie eine User-Klasse nutzen, die Benutzerentitäten darstellt. Diese Klasse speichert primäre Daten, z. B. Vorname, Nachname und Benutzer-ID. (Die ID ist erforderlich, da es sich um den Partitionsschlüssel zum Aktivieren von horizontaler Skalierung handelt.)

Jeder Benutzer verfügt über einige zugeordnete Versandeinstellungen und Coupons, sodass die ShippingPreference- und CouponsUsed-Datentypen diese Entitäten darstellen. Außerdem gibt es für die einzelnen ggf. Benutzer einen Bestellverlauf, der theoretisch unbegrenzt lang sein kann. Sie sollten daher eigene OrderHistory-Entitäten mit einer entsprechenden POJO-Java-Klasse verwenden.

Navigieren Sie zu src/main/java/com/azure/azure-cosmos-java-sql-app-mslearn, und untersuchen Sie den Ordner datatypes. Es werden mehrere POJO-Klassen angezeigt: User, ShippingPreference, OrderHistory und CouponsUsed. Sie haben also alle POJO-Entitätsklassen sowie die zugehörigen Hilfsklassen bereitgestellt!

Nun erstellen Sie einige Entitäten und führen einige einfache CRUD-Vorgänge für den Azure Cosmos DB-Container und die darin enthaltenen Dokumente aus. Sie können an Azure Cosmos DB eine Jackson-ObjectNode-Instanz übergeben, die das JSON-Dokument direkt angibt. Azure Cosmos DB kann POJO-Klassen in Java jedoch auch in JSON serialisieren. Es wird empfohlen, diesen Ansatz zu verwenden, da es sich dabei um die einfachste Möglichkeit handelt. Die gesamte restliche Vorgehensweise ist identisch.

Erstellen von Dokumenten

  1. Öffnen Sie User.java, und sehen Sie sich die Inhalte an. Der Bericht könnte beispielsweise wie folgt aussehen:

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

    Beachten Sie, dass die Zugriffsmethoden für id, userId und andere Felder implizit (nicht in Code definiert) sind. Dieses Verhalten ist möglich, da die Datenanmerkung @Data der Bibliothek „Project Lombok“ verwendet wird, um sie automatisch zu erstellen.

    Die @NoArgsConstructor-Anmerkung generiert einen Konstruktor ohne Argumente, der Standardfeldwerte festlegt. Die @AllArgsConstructor-Anmerkung generiert einen weiteren Konstruktor mit einem vollständigen Satz von Argumenten, um alle Feldwerte direkt festzulegen.

    Beachten Sie, dass User über eine id-Eigenschaft verfügt. Alle Azure Cosmos DB-Dokumente erfordern eine id-Eigenschaft. Deshalb müssen alle POJO-Klassen, die in JSON-Dokumente serialisiert werden sollen, ein id-Feld aufweisen.

  2. Fügen Sie die folgende Methode der Datei CosmosApp.java hinzu.

    /**
     * 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. Kehren Sie dann zur Methode basicOperations zurück, und fügen Sie Folgendes am Ende dieser Methode vor dem client.close()-Aufruf hinzu.

    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. Erstellen Sie CosmosApp.java in der IDE, und führen Sie das Programm aus, oder führen Sie das Programm im Terminal wie folgt aus:

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

    Im Terminal wird jeweils eine Ausgabe angezeigt, während die Anwendung die einzelnen neuen Benutzerdokumente erstellt.

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

    Möglicherweise wird von der Protokollierung zusätzlicher Text ausgegeben, z. B. Zeitstempel.

Glückwunsch! Sie haben Ihre ersten Daten in Azure Cosmos DB über eine Java-Anwendung erstellt. Pausieren Sie nun kurz, und bewerten Sie, was Sie bisher getan haben.

In basicOperations gibt es drei neue Aktionen:

  1. Erstellen der maxaxam User-Instanz.
  2. Erstellen der nelapin User-Instanz.
  3. Aufrufen von createUserDocumentsIfNotExist und Übergeben von maxaxam und nelapin in einer Liste.

Durch das Aufrufen von createUserDocumentsIfNotExist werden beide User-Instanzen als Elemente/Dokumente in Azure Cosmos DB eingefügt. Die Absicht hinter dem Übergeben der User-Instanzen als Liste ist die, eine leistungsfähige Methode für die schnelle Erfassung von POJO-Klassen in Azure Cosmos DB zu modellieren, wobei ein Minimum an Computeressourcen verwendet werden soll. createUserDocumentsIfNotExist implementiert ein effizientes asynchrones Einfügen einer Liste der POJO-Klassen.

Angenommen, Ihr Ziel bestünde darin, Anforderungen/Sekunde pro Thread zu maximieren. Zum Vergleich würde der synchrone Ansatz für das Schreiben von createUserDocumentsIfNotExist (hierbei soll für den Moment die readItem-Überprüfung ignoriert werden) darin bestehen, über jede User-Instanz in users zu iterieren. Für jedes User u-Element würden wir einen blockierenden Aufruf von createItem ausgeben:

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

Dieser synchrone Ansatz implementiert einen intuitiven Prozess: Anforderung ausgeben, auf Antwort warten, nächste Anforderung ausgeben. createUserDocumentsIfNotExist verwendet diesen Ansatz jedoch nicht, da das Blockieren von Aufrufen im Grunde genommen CPU-Zyklen während Anforderungsantwortzeiten verschwendet, was zu einer niedrigen Anzahl von Anforderungen/Sekunde führt.

Dieses Problem der Anforderungen/Sekunde können ggf. Sie umgehen, indem Sie mehrere Threads erzeugen, um parallele Aufrufe von Blockierungsanforderungen durchzuführen. Mehrere Threads führen zu einer Optimierung zur Ausführungszeit. Aber wenn es Ihr Ziel ist, sparsam mit Thread-Ressourcen umzugehen, ist dies immer noch verschwenderisch. Jeder Thread durchläuft während der Anforderungsantwortzeit Schleifen, wenn er stattdessen mehrere andere Aufgaben ausführen könnte – dies führt zu einer geringen Anzahl von Anforderungen pro Sekunde in jedem Thread.

Aus diesem Grund und um Ihnen den Zweck threadeffizienter Einfügevorgänge von POJO-Klassen in Java zu demonstrieren, steht hier stattdessen ein Beispiel für eine asynchrone Dokumenteneinfügung zur Verfügung. Die Unterstützung für asynchrone Vorgänge der Version 4 des Java SDK in Azure Cosmos DB stammt von Project Reactor, einem Java-Anwendungsframework, das ein streambasiertes Programmiermodell für deklarative Dataflows für eine asynchrone, ereignisgesteuerte Programmierung bereitstellt. createDocumentsIfNotExist implementiert die asynchrone Programmierung von Project Reactor.

In createUserDocumentsIfNotExist ist Flux.fromIterable(users) eine Project Reactor-Factorymethode. Es wird eine Flux-Instanz erstellt, die eine Quelle von asynchronen Ereignissen ist. In diesem Fall enthält jedes asynchrone „Ereignis“ ein User-Instanzargument. Die Flux-Instanz enthält zwei solche Ereignisse: eines für maxaxam und eines für nelapin. Der Code in .flatMap( ... ).blockLast(); definiert eine Pipeline von sequenziellen Vorgängen, die für die Ereignisse ausgeführt werden, die von der Flux-Instanz ausgegeben werden.

Einer dieser Vorgänge ist createItem. Die Idee ist, dass diese Pipeline fast identisch mit der synchronen Implementierung ist, mit dem Unterschied, dass beim createItem-Aufruf keine Blockierung erfolgt. Insbesondere der Aufruf von blockLast() abonniert die assemblierte Pipeline, was dazu führt, dass Flux seine beiden Ereignisse asynchron ausgibt. Anschließend verarbeitet die Pipeline in .flatMap( ... ).blockLast(); jedes dieser Ereignisse auf pseudoparallele Weise. Wenn eine Anforderung ausgegeben wird und auf eine Antwort wartet, verarbeitet Project Reactor andere Anforderungen im Hintergrund, was ein entscheidender Faktor für die Maximierung der Anzahl Anforderungen/Sekunde pro Thread ist.

Da Sie nun effiziente asynchrone Datenbankanforderungen mit Project Reactor gesehen haben, sollen der Einfachheit halber für den Rest dieses Labs blockierende (synchrone) Aufrufe verwendet werden. Weitere Informationen zu Project Reactor finden Sie im Leitfaden zum Reactor-Muster für Azure Cosmos DB.

Lesen von Dokumenten

  1. Wenn Sie Dokumente aus der Datenbank lesen möchten, fügen Sie CosmosApp die folgende Methode hinzu:

    /**
     * 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. Kopieren Sie den folgenden Code, und fügen Sie ihn am Ende der Methode basicOperations, nach dem Code für die Dokumenterstellung, ein:

    readUserDocument(maxaxam);
    
  3. Erstellen Sie CosmosApp.java in der IDE, und führen Sie das Programm aus, oder führen Sie das Programm im Terminal wie folgt aus:

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

    Das Terminal zeigt die folgende Ausgabe an, wobei „Read user 1“ angibt, dass das Dokument abgerufen wurde.

    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
    

    Möglicherweise wird von der Protokollierung auch zusätzlicher Text ausgegeben.

Ersetzen von Dokumenten

Azure Cosmos DB unterstützt das Ersetzen von JSON-Dokumenten. In diesem Fall wird ein Benutzerdatensatz aktualisiert, um eine Änderung an den Nachnamen zu berücksichtigen.

  1. Fügen Sie die replaceUserDocument-Methode nach der readUserDocument-Methode in der Datei CosmosApp.java hinzu.

    /**
     * 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. Kopieren Sie den folgenden Code, und fügen Sie ihn am Ende der Methode basicOperations, nach dem Code zum Lesen des Dokuments, ein:

    maxaxam.setLastName("Suh");
    replaceUserDocument(maxaxam);
    
  3. Erstellen Sie CosmosApp.java in der IDE, und führen Sie das Programm aus, oder führen Sie das Programm im Terminal wie folgt aus:

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

    Das Terminal zeigt die folgende Ausgabe an, wobei „Replaced last name for Suh“ angibt, dass das Dokument ersetzt wurde.

    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
    

Löschen von Dokumenten

  1. Kopieren Sie die deleteUserDocument-Methode, und fügen Sie sie unter Ihrer replaceUserDocument-Methode ein.

    /**
     * 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. Kopieren Sie den folgenden Code, und fügen Sie ihn am Ende der Methode basicOperations ein.

    deleteUserDocument(maxaxam);
    
  3. Erstellen Sie CosmosApp.java in der IDE, und führen Sie das Programm aus, oder führen Sie das Programm im Terminal wie folgt aus:

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

    Im Terminal wird die folgende Ausgabe angezeigt, wobei „Deleted user 1“ angibt, dass das Dokument gelöscht wurde.

    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
    

Programmgesteuertes Arbeiten mit Dokumenten

Daten werden in Azure Cosmos DB als JSON-Dokumente gespeichert. Dokumente können entweder im Portal erstellt, abgerufen, ersetzt oder gelöscht werden, oder programmgesteuert. In diesem Lab liegt der Fokus auf programmgesteuerten Vorgängen. All diese Vorgänge sind im Java SDK für Azure Cosmos DB verfügbar und auch über das Spring Data-Programmiermodell zugänglich. In diesem Modul verwenden wir Spring Data Azure Cosmos DB, um CRUD-Vorgänge (Create, Retrieve, Update, Delete – Erstellen, Abrufen, Aktualisieren, Löschen) für die in Azure Cosmos DB gespeicherten NoSQL-Daten auszuführen.

Die wichtigsten Vorgänge für Spring Data Azure Cosmos DB-Dokumente sind grundlegende Vorgänge im Spring Data-Programmiermodell:

  • save: Punktschreib- oder Aktualisierungsvorgang für ein Dokument, unabhängig davon, ob das Dokument bereits vorhanden ist
  • view: Punktlesevorgang für ein Dokument
  • delete: Punktlöschvorgang für ein Dokument

Sie benötigen Hilfsklassen (POJO-Klassen in Java), die die in der Datenbank gespeicherten Objekte darstellen, um diese Vorgänge durchführen zu können. Da wir hier mit einer Datenbank mit Onlinekunden arbeiten, sollten Sie die Klasse WebCustomer verwenden, um Benutzerentitäten zu repräsentieren. Diese Klasse speichert primäre Daten, z. B. Vorname, Nachname und Benutzer-ID. (Die ID ist erforderlich, da es sich um den Partitionsschlüssel zum Aktivieren von horizontaler Skalierung handelt.)

Jeder Webbenutzer verfügt über einige zugeordnete Versandeinstellungen und Coupons, sodass die Datentypen ShippingPreference und CouponsUsed diese Entitäten repräsentieren sollten. Außerdem gibt es für jeden Webbenutzer möglicherweise einen Bestellverlauf, der theoretisch unbegrenzt lang sein kann. Sie sollten daher eigene OrderHistory-Entitäten mit einer entsprechenden Java-POJO-Klasse verwenden.

Wechseln Sie zu src/main/java/com/azure/cosmos/examples/springexamples. Hier wird die POJO-Klasse WebCustomer angezeigt. Sehen Sie sich jetzt den Ordner common an. Hier werden mehrere POJO-Klassen angezeigt: ShippingPreference, OrderHistory und CouponsUsed. Sie haben also alle POJO-Entitätsklassen sowie die zugehörigen Hilfsklassen bereitgestellt!

Nun erstellen Sie einige Entitäten und führen einige einfache CRUD-Vorgänge für den Azure Cosmos DB-Container und die darin enthaltenen Dokumente aus. Sie können an Azure Cosmos DB eine Jackson-ObjectNode-Instanz übergeben, die das JSON-Dokument direkt angibt. Azure Cosmos DB kann POJO-Klassen in Java jedoch auch in JSON serialisieren. Es wird empfohlen, diesen Ansatz zu verwenden, da es sich dabei um die einfachste Möglichkeit handelt. Die gesamte restliche Vorgehensweise ist identisch.

Erstellen und Aktualisieren von Dokumenten

  1. Öffnen Sie WebCustomer.java, und sehen Sie sich den Inhalt an. Der Bericht könnte beispielsweise wie folgt aussehen:

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

    Beachten Sie, dass die Zugriffsmethoden für id, userId und andere Felder implizit (nicht in Code definiert) sind. Dieses Verhalten ist möglich, da die Datenanmerkung @Data der Bibliothek „Project Lombok“ verwendet wird, um sie automatisch zu erstellen.

    Die @NoArgsConstructor-Anmerkung generiert einen Konstruktor ohne Argumente, der Standardfeldwerte festlegt. Die @AllArgsConstructor-Anmerkung generiert einen weiteren Konstruktor mit einem vollständigen Satz von Argumenten, um alle Feldwerte direkt festzulegen.

    Beachten Sie, dass WebCustomer über eine id-Eigenschaft verfügt. Alle Azure Cosmos DB-Dokumente erfordern eine id-Eigenschaft. Deshalb müssen alle POJO-Klassen, die in JSON-Dokumente serialisiert werden sollen, ein id-Feld aufweisen.

  2. Fügen Sie CosmosSample.java die folgende Methode hinzu:

    /**
     * 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. Suchen Sie die run-Methode, und fügen Sie am Ende dieser Methode folgenden Code hinzu.

    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. Erstellen Sie CosmosSample.java in der IDE, und führen Sie das Programm aus, oder führen Sie das Programm wie folgt im Terminal aus:

    mvn clean package
    mvn spring-boot:run
    

    In der Terminalausgabe sehen Sie Folgendes:

    INFO: Creating WebCustomer 1
    INFO: Creating WebCustomer 2
    

Glückwunsch! Sie haben Ihre ersten Daten in Azure Cosmos DB mithilfe einer Java-Anwendung erstellt und/oder aktualisiert. Pausieren Sie nun kurz, und bewerten Sie, was Sie bisher getan haben.

In run gibt es drei neue Aktionen:

  1. Erstellen/Aktualisieren der WebCustomer-Instanz maxaxam.
  2. Erstellen/Aktualisieren der WebCustomer-Instanz nelapin.
  3. Aufrufen von createWebCustomerDocumentsIfNotExist und Übergeben von maxaxam und nelapin in einer Liste.

Durch das Aufrufen von createWebCustomerDocumentsIfNotExist werden beide WebCustomer-Instanzen als Elemente/Dokumente in Azure Cosmos DB eingefügt. Die Absicht hinter dem Übergeben der WebCustomer-Instanzen als Liste ist die, eine leistungsfähige Methode für die schnelle Erfassung von POJO-Klassen in Azure Cosmos DB zu modellieren, wobei ein Minimum an Computeressourcen verwendet werden soll. createWebCustomerDocumentsIfNotExist implementiert ein effizientes asynchrones Einfügen einer Liste der POJO-Klassen. Wenn eines der Dokumente bereits vorhanden ist, führt save eine Aktualisierung anstelle einer Dokumenterstellung aus.

Angenommen, Ihr Ziel bestünde darin, Anforderungen/Sekunde pro Thread zu maximieren. Zum Vergleich: Der synchrone Ansatz für das Schreiben von createWebCustomerDocumentsIfNotExist würde darin bestehen, jede WebCustomer-Instanz in webCustomers zu durchlaufen. Für jedes WebCustomer webCustomer-Element würden wir einen blockierenden Aufruf von save ausgeben:

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

Dieser synchrone Ansatz implementiert einen intuitiven Prozess: Anforderung ausgeben, auf Antwort warten, nächste Anforderung ausgeben. createWebCustomerDocumentsIfNotExist verwendet diesen Ansatz jedoch nicht, da das Blockieren von Aufrufen im Grunde genommen CPU-Zyklen während Anforderungsantwortzeiten verschwendet, was zu einer niedrigen Anzahl von Anforderungen/Sekunde führt.

Dieses Problem der Anforderungen/Sekunde können ggf. Sie umgehen, indem Sie mehrere Threads erzeugen, um parallele Aufrufe von Blockierungsanforderungen durchzuführen. Mehrere Threads führen zu einer Optimierung zur Ausführungszeit. Aber wenn es Ihr Ziel ist, sparsam mit Thread-Ressourcen umzugehen, ist dies immer noch verschwenderisch. Jeder Thread durchläuft während der Anforderungsantwortzeit Schleifen, wenn er stattdessen mehrere andere Aufgaben ausführen könnte – dies führt zu einer geringen Anzahl von Anforderungen pro Sekunde in jedem Thread.

Aus diesem Grund und um Ihnen den Zweck threadeffizienter Einfügevorgänge von POJO-Klassen in Java zu demonstrieren, steht hier stattdessen ein Beispiel für eine asynchrone Dokumenteneinfügung zur Verfügung. Die Unterstützung für asynchrone Vorgänge in Spring Data stammt von Project Reactor, einem Java-Anwendungsframework, das ein streambasiertes Programmiermodell für deklarative Dataflows für eine asynchrone ereignisgesteuerte Programmierung bereitstellt. createWebCustomerDocumentsIfNotExist implementiert die asynchrone Programmierung von Project Reactor.

In createWebCustomerDocumentsIfNotExist ist Flux.fromIterable(webCustomers) eine Project Reactor-Factorymethode. Es wird eine Flux-Instanz erstellt, die eine Quelle von asynchronen Ereignissen ist. In diesem Fall enthält jedes asynchrone „Ereignis“ ein WebCustomer-Instanzargument. Die Flux-Instanz enthält zwei solche Ereignisse: eines für maxaxam und eines für nelapin. Der Code in .flatMap( ... ).blockLast(); definiert eine Pipeline von sequenziellen Vorgängen, die für die Ereignisse ausgeführt werden, die von der Flux-Instanz ausgegeben werden.

In diesem Fall sind die beiden Vorgänge in der Pipeline save-Aufrufe. Die Idee ist, dass diese Pipeline fast identisch mit der synchronen Implementierung ist, mit dem Unterschied, dass beim save-Aufruf keine Blockierung erfolgt. Insbesondere der Aufruf von blockLast() abonniert die assemblierte Pipeline, was dazu führt, dass Flux seine beiden Ereignisse asynchron ausgibt. Anschließend verarbeitet die Pipeline in .flatMap( ... ).blockLast(); jedes dieser Ereignisse auf pseudoparallele Weise. Wenn eine Anforderung ausgegeben wird und auf eine Antwort wartet, verarbeitet Project Reactor andere Anforderungen im Hintergrund, was ein entscheidender Faktor für die Maximierung der Anzahl Anforderungen/Sekunde pro Thread ist.

Da Sie nun effiziente asynchrone Datenbankanforderungen mit Project Reactor gesehen haben, sollen der Einfachheit halber für den Rest dieses Labs blockierende asynchrone Aufrufe (die effektiv synchrone Aufrufe sind) verwendet werden. Weitere Informationen zu Project Reactor finden Sie im Leitfaden zum Reactor-Muster für Azure Cosmos DB.

Lesen von Dokumenten

  1. Wenn Sie Dokumente aus der Datenbank lesen möchten, fügen Sie CosmosSample die folgende Methode hinzu:

    /**
     * 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. Kopieren Sie den folgenden Code, und fügen Sie ihn am Ende der Methode run, nach dem Code für die Dokumenterstellung, ein:

    readWebCustomerDocument(maxaxam);
    
  3. Erstellen Sie CosmosSample.java in der IDE, und führen Sie das Programm aus, oder führen Sie das Programm wie folgt im Terminal aus:

    mvn clean package
    mvn spring-boot:run
    

    In der Terminalausgabe sehen Sie Folgendes: „Read user 1“ weist darauf hin, dass das Dokument abgerufen wurde.

    INFO: Read webCustomer 1
    

Löschen von Dokumenten

  1. Kopieren Sie die deleteWebCustomerDocument-Methode, und fügen Sie sie unter Ihrer readWebCustomerDocument-Methode ein.

    /**
     * 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. Kopieren Sie den folgenden Code, und fügen Sie ihn am Ende der Methode run ein.

    deleteWebCustomerDocument(maxaxam);
    
  3. Erstellen Sie CosmosSample.java in der IDE, und führen Sie das Programm aus, oder führen Sie das Programm wie folgt im Terminal aus:

    mvn clean package
    mvn spring-boot:run
    

    In der Terminalausgabe sehen Sie Folgendes: „Deleted user 1“ weist darauf hin, dass das Dokument gelöscht wurde.

    INFO: Deleted webCustomer 1
    

In dieser Lerneinheit haben Sie Dokumente in Ihrer Azure Cosmos DB-Datenbank erstellt, aktualisiert, gelesen und gelöscht.