Samouczek: tworzenie aplikacji internetowej Java przy użyciu usługi Azure Cosmos DB i interfejsu API dla NoSQL

DOTYCZY: NoSQL

W tym samouczku aplikacji internetowej w języku Java pokazano, jak przy użyciu usługi Microsoft Azure Cosmos DB przechowywać dane i uzyskiwać do nich dostęp z poziomu aplikacji w języku Java hostowanej w usłudze Azure App Service Web Apps. Bez karty kredytowej lub subskrypcji platformy Azure możesz skonfigurować bezpłatne konto Wypróbuj usługę Azure Cosmos DB. Z tego artykułu dowiesz się:

W tym samouczku aplikacji w języku Java pokazano, jak utworzyć aplikację do zarządzania zadaniami opartą na sieci Web, która pozwala na tworzenie, pobieranie i oznaczanie zadań jako ukończonych, jak pokazano na poniższej ilustracji. Każde z zadań z listy zadań do wykonania będzie przechowywane jako dokument JSON w usłudze Azure Cosmos DB.

Aplikacja My ToDo List w języku Java

Porada

Ten samouczek tworzenia aplikacji zakłada, że masz już pewne doświadczenie w korzystaniu z języka Java. Jeśli nie znasz języka Java lub wstępnie wymaganych narzędzi, zalecamy pobranie kompletnego projektu todo z usługi GitHub i skompilowanie go zgodnie z instrukcjami znajdującymi się na końcu artykułu. Po jego skompilowaniu możesz przejrzeć ten artykuł, aby przeanalizować kod w kontekście projektu.

Wymagania wstępne dotyczące tego samouczka aplikacji internetowej w języku Java

Przed rozpoczęciem korzystania z tego samouczka tworzenia aplikacji należy dysponować następującymi elementami:

Jeśli instalujesz te narzędzia po raz pierwszy, coreservlets.com zawiera przewodnik po procesie instalacji w sekcji Szybki start w artykule Samouczek: instalowanie serwera TomCat7 i korzystanie z niego za pomocą środowiska Eclipse .

Tworzenie konta usługi Azure Cosmos DB

Zacznijmy od utworzenia konta usługi Azure Cosmos DB. Jeśli masz już konto lub jeśli korzystasz z emulatora usługi Azure Cosmos DB na potrzeby tego samouczka, możesz od razu przejść do sekcji Krok 2. Tworzenie aplikacji Java JSP.

  1. W menu witryny Azure Portal lub na stronie głównej wybierz pozycję Utwórz zasób.

  2. Wyszukaj usługę Azure Cosmos DB. Wybierz pozycję Utwórz usługę>Azure Cosmos DB.

  3. Na stronie Tworzenie konta usługi Azure Cosmos DB wybierz opcję Utwórz w sekcji Azure Cosmos DB for NoSQL .

    Usługa Azure Cosmos DB udostępnia kilka interfejsów API:

    • NoSQL — dane dokumentu
    • PostgreSQL
    • MongoDB — dane dokumentu
    • Apache Cassandra
    • Tabela
    • Apache Gremlin — dla danych grafu

    Aby dowiedzieć się więcej na temat interfejsu API dla NoSQL, zobacz Witamy w usłudze Azure Cosmos DB.

  4. Na stronie Tworzenie konta usługi Azure Cosmos DB wprowadź podstawowe ustawienia nowego konta usługi Azure Cosmos DB.

    Ustawienie Wartość Opis
    Subskrypcja Nazwa subskrypcji Wybierz subskrypcję platformy Azure, której chcesz użyć dla tego konta usługi Azure Cosmos DB.
    Grupa zasobów Nazwa grupy zasobów Wybierz grupę zasobów lub wybierz pozycję Utwórz nową, a następnie wprowadź unikatową nazwę nowej grupy zasobów.
    Nazwa konta Unikatowa nazwa Wprowadź nazwę, aby zidentyfikować konto usługi Azure Cosmos DB. Ponieważ adres documents.azure.com jest dołączany do podanej nazwy w celu utworzenia identyfikatora URI, użyj unikatowej nazwy. Nazwa może zawierać tylko małe litery, cyfry i znak łącznika (-). Musi zawierać od 3 do 44 znaków.
    Lokalizacja Region najbliżej Twoich użytkowników Wybierz lokalizację geograficzną, w której będzie hostowane konto usługi Azure Cosmos DB. Użyj lokalizacji znajdującej się najbliżej Twoich użytkowników, aby zapewnić im najszybszy dostęp do danych.
    Tryb wydajności Aprowizowana przepływność lub bezserwerowa Wybierz pozycję Aprowizowana przepływność , aby utworzyć konto w trybie aprowizowanej przepływności . Wybierz pozycję Bezserwerowa , aby utworzyć konto w trybie bezserwerowym .
    Stosowanie rabatu za bezpłatną warstwę usługi Azure Cosmos DB Zastosuj lub nie zastosuj W przypadku warstwy Bezpłatna usługi Azure Cosmos DB uzyskasz bezpłatnie pierwsze 1000 RU/s i 25 GB miejsca do magazynowania na koncie. Dowiedz się więcej o warstwie Bezpłatna.
    Ograniczanie całkowitej przepływności konta Wybrane lub nie Ogranicz łączną przepływność, którą można aprowizować na tym koncie. Ten limit uniemożliwia nieoczekiwane opłaty związane z aprowizowaną przepływnością. Możesz zaktualizować lub usunąć ten limit po utworzeniu konta.

    Możesz mieć maksymalnie jedno konto usługi Azure Cosmos DB w warstwie Bezpłatna na subskrypcję platformy Azure i musi wyrazić zgodę podczas tworzenia konta. Jeśli nie widzisz opcji zastosowania rabatu w warstwie Bezpłatna, inne konto w subskrypcji zostało już włączone z warstwą Bezpłatna.

    Zrzut ekranu przedstawiający stronę Tworzenie konta usługi Azure Cosmos DB.

    Uwaga

    Następujące opcje nie są dostępne w przypadku wybrania opcji Bezserwerowa jako tryb pojemności:

    • Zastosuj rabat na podstawie warstwy Bezpłatna
    • Ograniczanie całkowitej przepływności konta
  5. Na karcie Dystrybucja globalna skonfiguruj następujące szczegóły. Możesz pozostawić wartości domyślne dla tego przewodnika Szybki start:

    Ustawienie Wartość Opis
    Nadmiarowość geograficzna Wyłącz Włącz lub wyłącz dystrybucję globalną na koncie, łącząc region z regionem pary. Później możesz dodać więcej regionów do swojego konta.
    Moduły zapisujące obsługujące wiele regionów Wyłącz Funkcja zapisu w wielu regionach umożliwia wykorzystanie aprowizowanej przepływności dla baz danych i kontenerów na całym świecie.
    Strefy dostępności Wyłącz Strefy dostępności pomóc zwiększyć dostępność i odporność aplikacji.

    Uwaga

    Następujące opcje nie są dostępne, jeśli wybierzesz opcję Bezserwerowy jako tryb pojemności na poprzedniej stronie Podstawowe :

    • Nadmiarowość geograficzna
    • Moduły zapisujące obsługujące wiele regionów
  6. Opcjonalnie możesz skonfigurować więcej szczegółów na następujących kartach:

    • Sieć. Konfigurowanie dostępu z sieci wirtualnej.
    • Zasady tworzenia kopii zapasowych. Skonfiguruj okresowe lub ciągłe zasady tworzenia kopii zapasowych .
    • Szyfrowanie. Użyj klucza zarządzanego przez usługę lub klucza zarządzanego przez klienta.
    • Tagi. Tagi to pary nazw/wartości, które umożliwiają kategoryzowanie zasobów i wyświetlanie skonsolidowanego rozliczeń przez zastosowanie tego samego tagu do wielu zasobów i grup zasobów.
  7. Wybierz pozycję Przejrzyj i utwórz.

  8. Przejrzyj ustawienia konta, a następnie wybierz pozycję Utwórz. Utworzenie konta trwa kilka minut. Poczekaj na wyświetlenie komunikatu Wdrożenie zostało ukończone na stronie portalu.

    Zrzut ekranu pokazuje, że wdrożenie zostało ukończone.

  9. Wybierz pozycję Przejdź do zasobu, aby przejść do strony konta usługi Azure Cosmos DB.

    Zrzut ekranu przedstawiający stronę konta usługi Azure Cosmos DB.

Przejdź do strony konta usługi Azure Cosmos DB i wybierz pozycję Klucze. Skopiuj wartości do użycia w utworzonej aplikacji internetowej.

Zrzut ekranu przedstawiający Azure Portal z wyróżnionym przyciskiem Klucze na stronie konta usługi Azure Cosmos DB

Tworzenie aplikacji Java JSP

Aby utworzyć aplikację JSP:

  1. Najpierw zaczniemy od utworzenia projektu Java. Uruchom środowisko Eclipse, a następnie wybierz pozycję Plik, wybierz pozycję Nowy, a następnie wybierz pozycję Dynamiczny projekt internetowy. Jeśli nie widzisz dynamicznego projektu sieci Web wymienionego jako dostępny projekt, wykonaj następujące czynności: wybierz pozycję Plik, wybierz pozycję Nowy, wybierz pozycję Projekt..., rozwiń węzeł Sieć Web, wybierz pozycję Dynamiczny projekt sieci Web, a następnie wybierz przycisk Dalej.

    Tworzenie aplikacji Java JSP

  2. Wprowadź nazwę projektu w polu Nazwa projektu, a następnie w menu rozwijanym Środowisko docelowe opcjonalnie wybierz wartość (np. Apache Tomcat v7.0), a następnie wybierz pozycję Zakończ. Wybranie docelowego środowiska uruchomieniowego umożliwia uruchamianie projektu lokalnie za pośrednictwem środowiska Eclipse.

  3. W środowisku Eclipse w widoku Project Explorer (Eksplorator projektów) rozwiń projekt. Kliknij prawym przyciskiem myszy pozycję WebContent, wybierz pozycję Nowy, a następnie wybierz pozycję Plik JSP.

  4. W oknie dialogowym New JSP File (Nowy plik JSP) nazwij plik index.jsp. Zachowaj folder nadrzędny jako WebContent, jak pokazano na poniższej ilustracji, a następnie wybierz przycisk Dalej.

    Tworzenie nowego pliku JSP — samouczek aplikacji internetowej w języku Java

  5. W oknie dialogowym Wybieranie szablonu JSP na potrzeby tego samouczka wybierz pozycję Nowy plik JSP (html), a następnie wybierz pozycję Zakończ.

  6. Po otwarciu pliku index.jsp w środowisku Eclipse dodaj tekst, aby wyświetlić Hello world! w istniejącym <body> elemecie. Zaktualizowana zawartość elementu <body> powinna wyglądać podobnie do następującego kodu:

    <body>
      <% out.println("Hello World!"); %>
    </body>
    
  7. Zapisz plik index.jsp .

  8. Jeśli ustawisz docelowe środowisko uruchomieniowe w kroku 2, możesz wybrać pozycję Projekt , a następnie uruchomić aplikację JSP lokalnie:

    Witaj świecie – samouczek aplikacji w języku Java

Instalowanie zestawu Java SDK usługi SQL

Najprostszym sposobem pobrania zestawu SDK Java usługi SQL i jego zależności jest skorzystanie z usługi Apache Maven. Aby to zrobić, należy przekonwertować projekt na projekt Maven, wykonując następujące kroki:

  1. Kliknij prawym przyciskiem myszy projekt w Eksploratorze projektów, wybierz pozycję Konfiguruj, wybierz pozycję Konwertuj na projekt Maven.

  2. W oknie Tworzenie nowego modułu POM zaakceptuj wartości domyślne, a następnie wybierz pozycję Zakończ.

  3. W widoku Project Explorer (Eksplorator projektów) otwórz plik pom.xml.

  4. Na karcie Zależności w okienku Zależności wybierz pozycję Dodaj.

  5. W oknie Select Dependency (Wybierz zależność) wykonaj następujące czynności:

    • W polu Identyfikator grupy wprowadź wartość com.azure.
    • W polu Identyfikator artefaktu wprowadź wartość azure-cosmos.
    • W polu Wersja wprowadź wartość 4.11.0.

    Możesz też dodać kod XML zależności dla identyfikatora grupy i identyfikatora artefaktu bezpośrednio do pliku pom.xml :

    <dependency>
      <groupId>com.azure</groupId>
      <artifactId>azure-cosmos</artifactId>
      <version>4.11.0</version>
    </dependency>
    
  6. Wybierz przycisk OK , a narzędzie Maven zainstaluje zestaw SQL Java SDK lub zapisze plik pom.xml.

Korzystanie z usługi Azure Cosmos DB w aplikacji Java

Teraz dodajmy modele, widoki i kontrolery do aplikacji internetowej.

Dodawanie modelu

Najpierw zdefiniujmy model w nowym pliku TodoItem.java. Klasa TodoItem definiuje schemat elementu wraz z metodami getter i setter:

package com.microsoft.azure.cosmos.sample.model;

//@Data
//@Builder
public class TodoItem {
    private String entityType;
    private String category;
    private boolean complete;
    private String id;
    private String name;

    public String getCategory() {
        return category;
    }

    public void setCategory(String category) {
        this.category = category;
    }

    public String getEntityType() {
        return entityType;
    }

    public void setEntityType(String entityType) {
        this.entityType = entityType;
    }

    public boolean isComplete() {
        return complete;
    }

    public void setComplete(boolean complete) {
        this.complete = complete;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    
}

Dodawanie klas obiektów dostępu do danych (DAO)

Utwórz obiekt dostępu do danych (DAO), aby abstrakcyjnie utrwalać elementy zadań do wykonania w usłudze Azure Cosmos DB. Aby zapisać zadania do wykonania w kolekcji, klient musi wiedzieć, w której bazie danych i kolekcji powinien je zachowywać (jak to jest wskazane przez linki do samego siebie). Ogólnie rzecz biorąc, najlepiej jest zapisać w pamięci podręcznej obiekty bazy danych i kolekcji, tam gdzie to jest możliwe, aby uniknąć dodatkowych połączeń do bazy danych.

  1. Aby wywołać usługę Azure Cosmos DB, należy utworzyć wystąpienie nowego cosmosClient obiektu. Ogólnie rzecz biorąc, najlepiej jest ponownie użyć cosmosClient obiektu, a nie konstruować nowego klienta dla każdego kolejnego żądania. Możesz ponownie użyć klienta, definiując go w cosmosClientFactory klasie. Zaktualizuj wartości HOST i MASTER_KEY zapisane w kroku 1. Zastąp zmienną HOST identyfikatorem URI i zastąp MASTER_KEY kluczem PODSTAWOWYM. Użyj następującego kodu, aby utworzyć klasę CosmosClientFactory w pliku CosmosClientFactory.java :

    package com.microsoft.azure.cosmos.sample.dao;
    
    import com.azure.cosmos.ConsistencyLevel;
    import com.azure.cosmos.CosmosClient;
    import com.azure.cosmos.CosmosClientBuilder;
    
    public class CosmosClientFactory {
        private static final String HOST = "[ACCOUNT HOST NAME]";
        private static final String MASTER_KEY = "[ACCOUNT KEY]";
    
        private static CosmosClient cosmosClient = new CosmosClientBuilder()
                .endpoint(HOST)
                .key(MASTER_KEY)
                .consistencyLevel(ConsistencyLevel.EVENTUAL)
                .buildClient();
    
        public static CosmosClient getCosmosClient() {
            return cosmosClient;
        }
    
    }
    
  2. Utwórz nowy plik TodoDao.java i dodaj klasę TodoDao do tworzenia, aktualizowania, odczytywania i usuwania elementów zadań do wykonania:

    package com.microsoft.azure.cosmos.sample.dao;
    
    import java.util.List;
    
    import com.microsoft.azure.cosmos.sample.model.TodoItem;
    
    public interface TodoDao {
        /**
         * @return A list of TodoItems
         */
        public List<TodoItem> readTodoItems();
    
        /**
         * @param todoItem
         * @return whether the todoItem was persisted.
         */
        public TodoItem createTodoItem(TodoItem todoItem);
    
        /**
         * @param id
         * @return the TodoItem
         */
        public TodoItem readTodoItem(String id);
    
        /**
         * @param id
         * @return the TodoItem
         */
        public TodoItem updateTodoItem(String id, boolean isComplete);
    
        /**
         *
         * @param id
         * @return whether the delete was successful.
         */
        public boolean deleteTodoItem(String id);
    }
    
  3. Utwórz nowy plik MockDao.java i dodaj klasę MockDao . Ta klasa implementuje TodoDao klasę w celu wykonywania operacji CRUD na elementach:

    package com.microsoft.azure.cosmos.sample.dao;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    import lombok.NonNull;
    
    import com.microsoft.azure.cosmos.sample.model.TodoItem;
    
    public class MockDao implements TodoDao {
        private final Map<String, TodoItem> todoItemMap;
    
        public MockDao() {
            todoItemMap = new HashMap<String, TodoItem>();
        }
    
        @Override
        public TodoItem createTodoItem(@NonNull TodoItem todoItem) {
            if (todoItem.getId() == null || todoItem.getId().isEmpty()) {
                todoItem.setId(generateId());
            }
            todoItemMap.put(todoItem.getId(), todoItem);
            return todoItem;
        }
    
        @Override
        public TodoItem readTodoItem(@NonNull String id) {
            return todoItemMap.get(id);
        }
    
        @Override
        public List<TodoItem> readTodoItems() {
            return new ArrayList<TodoItem>(todoItemMap.values());
        }
    
        @Override
        public TodoItem updateTodoItem(String id, boolean isComplete) {
            todoItemMap.get(id).setComplete(isComplete);
            return todoItemMap.get(id);
        }
    
        @Override
        public boolean deleteTodoItem(@NonNull String id) {
            todoItemMap.remove(id);
            return true;
        }
    
        private String generateId() {
            return new Integer(todoItemMap.size()).toString();
        }
    }
    
  4. Utwórz nowy plik DocDbDao.java i dodaj klasę DocDbDao . Ta klasa definiuje kod do utrwalania elementów TodoItems w kontenerze, pobiera bazę danych i kolekcję, jeśli istnieje lub tworzy nowy, jeśli nie istnieje. W tym przykładzie użyto formatu Gson do serializacji i de serializacji dokumentów TodoItem Plain Old Java Objects (POJO) do dokumentów JSON. Aby zapisać zadania do wykonania w kolekcji, klient musi wiedzieć, w której bazie danych i kolekcji powinien je zachowywać (jak to jest wskazane przez linki do samego siebie). Ta klasa definiuje również funkcję pomocnika w celu pobrania dokumentów przez inny atrybut (np. "ID") zamiast linku samodzielnego. Możesz użyć metody pomocnika, aby pobrać dokument TodoItem JSON według identyfikatora, a następnie deserializacji go do poJO.

    Można również użyć cosmosClient obiektu klienta, aby pobrać kolekcję lub listę zadań TodoItems przy użyciu zapytania SQL. Na koniec zdefiniujesz metodę delete, aby usunąć element TodoItem z listy. Poniższy kod przedstawia zawartość DocDbDao klasy:

    package com.microsoft.azure.cosmos.sample.dao;
    
    import com.azure.cosmos.CosmosClient;
    import com.azure.cosmos.CosmosContainer;
    import com.azure.cosmos.CosmosDatabase;
    import com.azure.cosmos.CosmosException;
    import com.azure.cosmos.implementation.Utils;
    import com.azure.cosmos.models.CosmosContainerProperties;
    import com.azure.cosmos.models.CosmosContainerResponse;
    import com.azure.cosmos.models.CosmosDatabaseResponse;
    import com.azure.cosmos.models.CosmosItemRequestOptions;
    import com.azure.cosmos.models.CosmosQueryRequestOptions;
    import com.azure.cosmos.models.FeedResponse;
    import com.azure.cosmos.models.PartitionKey;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.node.ObjectNode;
    import com.google.gson.Gson;
    import com.microsoft.azure.cosmos.sample.model.TodoItem;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class DocDbDao implements TodoDao {
        // The name of our database.
        private static final String DATABASE_ID = "TestDB";
    
        // The name of our collection.
        private static final String CONTAINER_ID = "TestCollection";
    
        // We'll use Gson for POJO <=> JSON serialization for this example.
        private static Gson gson = new Gson();
    
        // The Cosmos DB Client
        private static CosmosClient cosmosClient = CosmosClientFactory
            .getCosmosClient();
    
        // The Cosmos DB database
        private static CosmosDatabase cosmosDatabase = null;
    
        // The Cosmos DB container
        private static CosmosContainer cosmosContainer = null;
    
        // For POJO/JsonNode interconversion
        private static final ObjectMapper OBJECT_MAPPER = Utils.getSimpleObjectMapper();
    
        @Override
        public TodoItem createTodoItem(TodoItem todoItem) {
            // Serialize the TodoItem as a JSON Document.
    
            JsonNode todoItemJson = OBJECT_MAPPER.valueToTree(todoItem);
    
            ((ObjectNode) todoItemJson).put("entityType", "todoItem");
    
            try {
                // Persist the document using the DocumentClient.
                todoItemJson =
                    getContainerCreateResourcesIfNotExist()
                        .createItem(todoItemJson)
                        .getItem();
            } catch (CosmosException e) {
                System.out.println("Error creating TODO item.\n");
                e.printStackTrace();
                return null;
            }
    
    
            try {
    
                return OBJECT_MAPPER.treeToValue(todoItemJson, TodoItem.class);
                //return todoItem;
            } catch (Exception e) {
                System.out.println("Error deserializing created TODO item.\n");
                e.printStackTrace();
    
                return null;
            }
    
        }
    
        @Override
        public TodoItem readTodoItem(String id) {
            // Retrieve the document by id using our helper method.
            JsonNode todoItemJson = getDocumentById(id);
    
            if (todoItemJson != null) {
                // De-serialize the document in to a TodoItem.
                try {
                    return OBJECT_MAPPER.treeToValue(todoItemJson, TodoItem.class);
                } catch (JsonProcessingException e) {
                    System.out.println("Error deserializing read TODO item.\n");
                    e.printStackTrace();
    
                    return null;
                }
            } else {
                return null;
            }
        }
    
        @Override
        public List<TodoItem> readTodoItems() {
    
            List<TodoItem> todoItems = new ArrayList<TodoItem>();
    
            String sql = "SELECT * FROM root r WHERE r.entityType = 'todoItem'";
            int maxItemCount = 1000;
            int maxDegreeOfParallelism = 1000;
            int maxBufferedItemCount = 100;
    
            CosmosQueryRequestOptions options = new CosmosQueryRequestOptions();
            options.setMaxBufferedItemCount(maxBufferedItemCount);
            options.setMaxDegreeOfParallelism(maxDegreeOfParallelism);
            options.setQueryMetricsEnabled(false);
    
            int error_count = 0;
            int error_limit = 10;
    
            String continuationToken = null;
            do {
    
                for (FeedResponse<JsonNode> pageResponse :
                    getContainerCreateResourcesIfNotExist()
                        .queryItems(sql, options, JsonNode.class)
                        .iterableByPage(continuationToken, maxItemCount)) {
    
                    continuationToken = pageResponse.getContinuationToken();
    
                    for (JsonNode item : pageResponse.getElements()) {
    
                        try {
                            todoItems.add(OBJECT_MAPPER.treeToValue(item, TodoItem.class));
                        } catch (JsonProcessingException e) {
                            if (error_count < error_limit) {
                                error_count++;
                                if (error_count >= error_limit) {
                                    System.out.println("\n...reached max error count.\n");
                                } else {
                                    System.out.println("Error deserializing TODO item JsonNode. " +
                                        "This item will not be returned.");
                                    e.printStackTrace();
                                }
                            }
                        }
    
                    }
                }
    
            } while (continuationToken != null);
    
            return todoItems;
        }
    
        @Override
        public TodoItem updateTodoItem(String id, boolean isComplete) {
            // Retrieve the document from the database
            JsonNode todoItemJson = getDocumentById(id);
    
            // You can update the document as a JSON document directly.
            // For more complex operations - you could de-serialize the document in
            // to a POJO, update the POJO, and then re-serialize the POJO back in to
            // a document.
            ((ObjectNode) todoItemJson).put("complete", isComplete);
    
            try {
                // Persist/replace the updated document.
                todoItemJson =
                    getContainerCreateResourcesIfNotExist()
                        .replaceItem(todoItemJson, id, new PartitionKey(id), new CosmosItemRequestOptions())
                        .getItem();
            } catch (CosmosException e) {
                System.out.println("Error updating TODO item.\n");
                e.printStackTrace();
                return null;
            }
    
            // De-serialize the document in to a TodoItem.
            try {
                return OBJECT_MAPPER.treeToValue(todoItemJson, TodoItem.class);
            } catch (JsonProcessingException e) {
                System.out.println("Error deserializing updated item.\n");
                e.printStackTrace();
    
                return null;
            }
        }
    
        @Override
        public boolean deleteTodoItem(String id) {
            // CosmosDB refers to documents by self link rather than id.
    
            // Query for the document to retrieve the self link.
            JsonNode todoItemJson = getDocumentById(id);
    
            try {
                // Delete the document by self link.
                getContainerCreateResourcesIfNotExist()
                    .deleteItem(id, new PartitionKey(id), new CosmosItemRequestOptions());
            } catch (CosmosException e) {
                System.out.println("Error deleting TODO item.\n");
                e.printStackTrace();
                return false;
            }
    
            return true;
        }
    
        /*
        
        private CosmosDatabase getTodoDatabase() {
            if (databaseCache == null) {
                // Get the database if it exists
                List<CosmosDatabase> databaseList = cosmosClient
                        .queryDatabases(
                                "SELECT * FROM root r WHERE r.id='" + DATABASE_ID
                                        + "'", null).getQueryIterable().toList();
    
                if (databaseList.size() > 0) {
                    // Cache the database object so we won't have to query for it
                    // later to retrieve the selfLink.
                    databaseCache = databaseList.get(0);
                } else {
                    // Create the database if it doesn't exist.
                    try {
                        CosmosDatabase databaseDefinition = new CosmosDatabase();
                        databaseDefinition.setId(DATABASE_ID);
    
                        databaseCache = cosmosClient.createDatabase(
                                databaseDefinition, null).getResource();
                    } catch (CosmosException e) {
                        // TODO: Something has gone terribly wrong - the app wasn't
                        // able to query or create the collection.
                        // Verify your connection, endpoint, and key.
                        e.printStackTrace();
                    }
                }
            }
    
            return databaseCache;
        }
    
        */
    
        private CosmosContainer getContainerCreateResourcesIfNotExist() {
    
            try {
    
                if (cosmosDatabase == null) {
                    CosmosDatabaseResponse cosmosDatabaseResponse = cosmosClient.createDatabaseIfNotExists(DATABASE_ID);
                    cosmosDatabase = cosmosClient.getDatabase(cosmosDatabaseResponse.getProperties().getId());
                }
    
            } catch (CosmosException e) {
                // TODO: Something has gone terribly wrong - the app wasn't
                // able to query or create the collection.
                // Verify your connection, endpoint, and key.
                System.out.println("Something has gone terribly wrong - " +
                    "the app wasn't able to create the Database.\n");
                e.printStackTrace();
            }
    
            try {
    
                if (cosmosContainer == null) {
                    CosmosContainerProperties properties = new CosmosContainerProperties(CONTAINER_ID, "/id");
                    CosmosContainerResponse cosmosContainerResponse = cosmosDatabase.createContainerIfNotExists(properties);
                    cosmosContainer = cosmosDatabase.getContainer(cosmosContainerResponse.getProperties().getId());
                }
    
            } catch (CosmosException e) {
                // TODO: Something has gone terribly wrong - the app wasn't
                // able to query or create the collection.
                // Verify your connection, endpoint, and key.
                System.out.println("Something has gone terribly wrong - " +
                    "the app wasn't able to create the Container.\n");
                e.printStackTrace();
            }
    
            return cosmosContainer;
        }
    
        private JsonNode getDocumentById(String id) {
    
            String sql = "SELECT * FROM root r WHERE r.id='" + id + "'";
            int maxItemCount = 1000;
            int maxDegreeOfParallelism = 1000;
            int maxBufferedItemCount = 100;
    
            CosmosQueryRequestOptions options = new CosmosQueryRequestOptions();
            options.setMaxBufferedItemCount(maxBufferedItemCount);
            options.setMaxDegreeOfParallelism(maxDegreeOfParallelism);
            options.setQueryMetricsEnabled(false);
    
            List<JsonNode> itemList = new ArrayList();
    
            String continuationToken = null;
            do {
                for (FeedResponse<JsonNode> pageResponse :
                    getContainerCreateResourcesIfNotExist()
                        .queryItems(sql, options, JsonNode.class)
                        .iterableByPage(continuationToken, maxItemCount)) {
    
                    continuationToken = pageResponse.getContinuationToken();
    
                    for (JsonNode item : pageResponse.getElements()) {
                        itemList.add(item);
                    }
                }
    
            } while (continuationToken != null);
    
            if (itemList.size() > 0) {
                return itemList.get(0);
            } else {
                return null;
            }
        }
    
    }
    
  5. Następnie utwórz nowy plik TodoDaoFactory.java i dodaj TodoDaoFactory klasę, która tworzy nowy obiekt DocDbDao:

    package com.microsoft.azure.cosmos.sample.dao;
    
    public class TodoDaoFactory {
        private static TodoDao myTodoDao = new DocDbDao();
    
        public static TodoDao getDao() {
            return myTodoDao;
        }
    }
    

Dodawanie kontrolera

Dodaj kontroler TodoItemController do aplikacji. W tym projekcie użyto Projektu Lombok, aby wygenerować konstruktor, metody pobierające, metody ustawiające i konstruktora. Możesz też napisać ten kod ręcznie lub wygenerować go za pomocą środowiska IDE:

package com.microsoft.azure.cosmos.sample.controller;

import java.util.List;
import java.util.UUID;

import lombok.NonNull;

import com.microsoft.azure.cosmos.sample.dao.TodoDao;
import com.microsoft.azure.cosmos.sample.dao.TodoDaoFactory;
import com.microsoft.azure.cosmos.sample.model.TodoItem;

public class TodoItemController {
    public static TodoItemController getInstance() {
        if (todoItemController == null) {
            todoItemController = new TodoItemController(TodoDaoFactory.getDao());
        }
        return todoItemController;
    }

    private static TodoItemController todoItemController;

    private final TodoDao todoDao;

    TodoItemController(TodoDao todoDao) {
        this.todoDao = todoDao;
    }

    public TodoItem createTodoItem(@NonNull String name,
            @NonNull String category, boolean isComplete) {
        TodoItem todoItem = new TodoItem();
        
        todoItem.setName(name);
        todoItem.setCategory(category);
        todoItem.setComplete(isComplete);
        todoItem.setId(UUID.randomUUID().toString());

        return todoDao.createTodoItem(todoItem);
    }

    public boolean deleteTodoItem(@NonNull String id) {
        return todoDao.deleteTodoItem(id);
    }

    public TodoItem getTodoItemById(@NonNull String id) {
        return todoDao.readTodoItem(id);
    }

    public List<TodoItem> getTodoItems() {
        return todoDao.readTodoItems();
    }

    public TodoItem updateTodoItem(@NonNull String id, boolean isComplete) {
        return todoDao.updateTodoItem(id, isComplete);
    }
}

Tworzenie serwletu

Następnie utwórz serwlet służącą do kierowania żądań HTTP do kontrolera. Utwórz plik ApiServlet.java i zdefiniuj w nim następujący kod:

package com.microsoft.azure.cosmos.sample;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.gson.Gson;
import com.microsoft.azure.cosmos.sample.controller.TodoItemController;

/**
 * API Frontend Servlet
 */
@WebServlet("/api")
public class ApiServlet extends HttpServlet {
    // API Keys
    public static final String API_METHOD = "method";

    // API Methods
    public static final String CREATE_TODO_ITEM = "createTodoItem";
    public static final String GET_TODO_ITEMS = "getTodoItems";
    public static final String UPDATE_TODO_ITEM = "updateTodoItem";

    // API Parameters
    public static final String TODO_ITEM_ID = "todoItemId";
    public static final String TODO_ITEM_NAME = "todoItemName";
    public static final String TODO_ITEM_CATEGORY = "todoItemCategory";
    public static final String TODO_ITEM_COMPLETE = "todoItemComplete";

    public static final String MESSAGE_ERROR_INVALID_METHOD = "{'error': 'Invalid method'}";

    private static final long serialVersionUID = 1L;
    private static final Gson gson = new Gson();

    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        String apiResponse = MESSAGE_ERROR_INVALID_METHOD;

        TodoItemController todoItemController = TodoItemController
                .getInstance();

        String id = request.getParameter(TODO_ITEM_ID);
        String name = request.getParameter(TODO_ITEM_NAME);
        String category = request.getParameter(TODO_ITEM_CATEGORY);
        String itemComplete = request.getParameter(TODO_ITEM_COMPLETE);
        boolean isComplete = itemComplete!= null && itemComplete.equalsIgnoreCase("true");

        switch (request.getParameter(API_METHOD)) {
        case CREATE_TODO_ITEM:
            apiResponse = gson.toJson(todoItemController.createTodoItem(name,
                    category, isComplete));
            break;
        case GET_TODO_ITEMS:
            apiResponse = gson.toJson(todoItemController.getTodoItems());
            break;
        case UPDATE_TODO_ITEM:
            apiResponse = gson.toJson(todoItemController.updateTodoItem(id,
                    isComplete));
            break;
        default:
            break;
        }

        response.setCharacterEncoding("UTF-8");
        response.getWriter().println(apiResponse);
    }

    @Override
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

Łączenie reszty aplikacji Java

Teraz, gdy skończyliśmy zabawne bity, wszystko, co pozostało, to utworzyć szybki interfejs użytkownika i połączyć go z dao.

  1. Do wyświetlenia użytkownikowi potrzebny jest internetowy interfejs użytkownika. Napiszmy ponownie utworzony wcześniej plik index.jsp z następującym kodem:

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge;" />
      <title>Azure Cosmos Java Sample</title>
    
      <!-- Bootstrap -->
      <link href="//ajax.aspnetcdn.com/ajax/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
    
      <style>
        /* Add padding to body for fixed nav bar */
        body {
          padding-top: 50px;
        }
      </style>
    </head>
    <body>
      <!-- Nav Bar -->
      <div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
        <div class="container">
          <div class="navbar-header">
            <a class="navbar-brand" href="#">My Tasks</a>
          </div>
        </div>
      </div>
    
      <!-- Body -->
      <div class="container">
        <h1>My ToDo List</h1>
    
        <hr/>
    
        <!-- The ToDo List -->
        <div class = "todoList">
          <table class="table table-bordered table-striped" id="todoItems">
            <thead>
              <tr>
                <th>Name</th>
                <th>Category</th>
                <th>Complete</th>
              </tr>
            </thead>
            <tbody>
            </tbody>
          </table>
    
          <!-- Update Button -->
          <div class="todoUpdatePanel">
            <form class="form-horizontal" role="form">
              <button type="button" class="btn btn-primary">Update Tasks</button>
            </form>
          </div>
    
        </div>
    
        <hr/>
    
        <!-- Item Input Form -->
        <div class="todoForm">
          <form class="form-horizontal" role="form">
            <div class="form-group">
              <label for="inputItemName" class="col-sm-2">Task Name</label>
              <div class="col-sm-10">
                <input type="text" class="form-control" id="inputItemName" placeholder="Enter name">
              </div>
            </div>
    
            <div class="form-group">
              <label for="inputItemCategory" class="col-sm-2">Task Category</label>
              <div class="col-sm-10">
                <input type="text" class="form-control" id="inputItemCategory" placeholder="Enter category">
              </div>
            </div>
    
            <button type="button" class="btn btn-primary">Add Task</button>
          </form>
        </div>
    
      </div>
    
      <!-- Placed at the end of the document so the pages load faster -->
      <script src="//ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.1.min.js"></script>
      <script src="//ajax.aspnetcdn.com/ajax/bootstrap/3.2.0/bootstrap.min.js"></script>
      <script src="assets/todo.js"></script>
    </body>
    </html>
    
  2. Na koniec napisz kod JavaScript po stronie klienta, aby powiązać internetowy interfejs użytkownika i serwlet ze sobą:

    /**
     * ToDo App
     */
    
    var todoApp = {
      /*
       * API methods to call Java backend.
       */
      apiEndpoint: "api",
    
      createTodoItem: function(name, category, isComplete) {
        $.post(todoApp.apiEndpoint, {
            "method": "createTodoItem",
            "todoItemName": name,
            "todoItemCategory": category,
            "todoItemComplete": isComplete
          },
          function(data) {
            var todoItem = data;
            todoApp.addTodoItemToTable(todoItem.id, todoItem.name, todoItem.category, todoItem.complete);
          },
          "json");
      },
    
      getTodoItems: function() {
        $.post(todoApp.apiEndpoint, {
            "method": "getTodoItems"
          },
          function(data) {
            var todoItemArr = data;
            $.each(todoItemArr, function(index, value) {
              todoApp.addTodoItemToTable(value.id, value.name, value.category, value.complete);
            });
          },
          "json");
      },
    
      updateTodoItem: function(id, isComplete) {
        $.post(todoApp.apiEndpoint, {
            "method": "updateTodoItem",
            "todoItemId": id,
            "todoItemComplete": isComplete
          },
          function(data) {},
          "json");
      },
    
      /*
       * UI Methods
       */
      addTodoItemToTable: function(id, name, category, isComplete) {
        var rowColor = isComplete ? "active" : "warning";
    
        todoApp.ui_table().append($("<tr>")
          .append($("<td>").text(name))
          .append($("<td>").text(category))
          .append($("<td>")
            .append($("<input>")
              .attr("type", "checkbox")
              .attr("id", id)
              .attr("checked", isComplete)
              .attr("class", "isComplete")
            ))
          .addClass(rowColor)
        );
      },
    
      /*
       * UI Bindings
       */
      bindCreateButton: function() {
        todoApp.ui_createButton().click(function() {
          todoApp.createTodoItem(todoApp.ui_createNameInput().val(), todoApp.ui_createCategoryInput().val(), false);
          todoApp.ui_createNameInput().val("");
          todoApp.ui_createCategoryInput().val("");
        });
      },
    
      bindUpdateButton: function() {
        todoApp.ui_updateButton().click(function() {
          // Disable button temporarily.
          var myButton = $(this);
          var originalText = myButton.text();
          $(this).text("Updating...");
          $(this).prop("disabled", true);
    
          // Call api to update todo items.
          $.each(todoApp.ui_updateId(), function(index, value) {
            todoApp.updateTodoItem(value.name, value.value);
            $(value).remove();
          });
    
          // Re-enable button.
          setTimeout(function() {
            myButton.prop("disabled", false);
            myButton.text(originalText);
          }, 500);
        });
      },
    
      bindUpdateCheckboxes: function() {
        todoApp.ui_table().on("click", ".isComplete", function(event) {
          var checkboxElement = $(event.currentTarget);
          var rowElement = $(event.currentTarget).parents('tr');
          var id = checkboxElement.attr('id');
          var isComplete = checkboxElement.is(':checked');
    
          // Togle table row color
          if (isComplete) {
            rowElement.addClass("active");
            rowElement.removeClass("warning");
          } else {
            rowElement.removeClass("active");
            rowElement.addClass("warning");
          }
    
          // Update hidden inputs for update panel.
          todoApp.ui_updateForm().children("input[name='" + id + "']").remove();
    
          todoApp.ui_updateForm().append($("<input>")
            .attr("type", "hidden")
            .attr("class", "updateComplete")
            .attr("name", id)
            .attr("value", isComplete));
    
        });
      },
    
      /*
       * UI Elements
       */
      ui_createNameInput: function() {
        return $(".todoForm #inputItemName");
      },
    
      ui_createCategoryInput: function() {
        return $(".todoForm #inputItemCategory");
      },
    
      ui_createButton: function() {
        return $(".todoForm button");
      },
    
      ui_table: function() {
        return $(".todoList table tbody");
      },
    
      ui_updateButton: function() {
        return $(".todoUpdatePanel button");
      },
    
      ui_updateForm: function() {
        return $(".todoUpdatePanel form");
      },
    
      ui_updateId: function() {
        return $(".todoUpdatePanel .updateComplete");
      },
    
      /*
       * Install the TodoApp
       */
      install: function() {
        todoApp.bindCreateButton();
        todoApp.bindUpdateButton();
        todoApp.bindUpdateCheckboxes();
    
        todoApp.getTodoItems();
      }
    };
    
    $(document).ready(function() {
      todoApp.install();
    });
    
  3. Teraz pozostało już tylko przetestować aplikację. Uruchom aplikację lokalnie, a następnie dodaj jakieś zadania do wykonania, wpisując nazwę zadania oraz jego kategorię i klikając przycisk Add Task (Dodaj zadanie). Po pojawieniu się elementu można zaktualizować, czy zostało ukończone, przełączając pole wyboru i klikając pozycję Aktualizuj zadania.

Wdrożenie aplikacji w języku Java w usłudze Azure Web Sites

Usługa Azure Web Sites sprawia, że wdrożenie aplikacji Java sprowadza się do wyeksportowania aplikacji jako pliku WAR i przesłania go na serwer za pomocą systemu kontroli źródła (np. Git) lub FTP.

  1. Aby wyeksportować aplikację jako plik WAR, kliknij prawym przyciskiem myszy projekt w Eksploratorze projektów, wybierz pozycję Eksportuj, a następnie wybierz pozycję Plik WAR.

  2. W oknie WAR Export (Eksport pliku WAR) wykonaj następujące czynności:

    • W polu Projekt internetowy wprowadź ciąg azure-cosmos-java-sample.
    • W polu Miejsce docelowe wybierz miejsce zapisania pliku WAR.
    • Wybierz pozycję Zakończ.
  3. Teraz, gdy masz już plik WAR, możesz go po prostu przesłać do katalogu webapps swojej usługi Azure Web Sites. Aby uzyskać instrukcje dotyczące przekazywania pliku, zobacz Add a Java application to Azure App Service Web Apps (Dodawanie aplikacji Java do usługi Web Apps w usłudze Azure App Service). Po przekazaniu pliku WAR do katalogu webapps środowisko uruchomieniowe wykryje, że został dodany i automatycznie go załaduje.

  4. Aby wyświetlić gotową aplikację, przejdź do http://YOUR\_SITE\_NAME.azurewebsites.net/azure-cosmos-java-sample/ i zacznij dodawać zadania.

Pobieranie projektu z usługi GitHub

Wszystkie przykłady w tym samouczku są zawarte w projekcie todo w usłudze GitHub. Aby zaimportować projekt todo do środowiska Eclipse, upewnij się, że dysponujesz oprogramowaniem i zasobami wymienionymi w sekcji Wymagania wstępne, a następnie wykonaj następujące czynności:

  1. Zainstaluj Projekt Lombok. Lombok służy w projekcie do generowania konstruktorów, metod pobierających i ustawiających. Po pobraniu pliku lombok.jar kliknij go dwukrotnie, aby go zainstalować, lub zainstaluj go przy użyciu wiersza polecenia.

  2. Jeśli środowisko Eclipse jest otwarte, zamknij je i uruchom ponownie, aby załadować Lombok.

  3. W środowisku Eclipse w menu Plik wybierz pozycję Importuj.

  4. W oknie Importowanie wybierz pozycję Git, wybierz pozycję Projekty z usługi Git, a następnie wybierz pozycję Dalej.

  5. Na ekranie Wybieranie źródła repozytorium wybierz pozycję Klonuj identyfikator URI.

  6. Na ekranie Źródłowe repozytorium Git w polu Identyfikator URI wprowadź , https://github.com/Azure-Samples/azure-cosmos-java-sql-api-todo-appa następnie wybierz przycisk Dalej.

  7. Na ekranie Wybór gałęzi upewnij się, że jest zaznaczone pole główne , a następnie wybierz przycisk Dalej.

  8. Na ekranie Miejsce docelowe lokalne wybierz pozycję Przeglądaj , aby wybrać folder, w którym można skopiować repozytorium, a następnie wybierz przycisk Dalej.

  9. Na ekranie Wybieranie kreatora do użycia do importowania projektów upewnij się, że wybrano opcję Importuj istniejące projekty , a następnie wybierz przycisk Dalej.

  10. Na ekranie Importowanie projektów usuń zaznaczenie projektu DocumentDB , a następnie wybierz pozycję Zakończ. Projekt DocumentDB zawiera zestaw SDK Java usługi Azure Cosmos DB, który zamiast tego zostanie dodany jako zależność.

  11. W Eksploratorze projektów przejdź do pozycji azure-cosmos-java-sample\src\com.microsoft.azure.cosmos.sample.dao\DocumentClientFactory.java i zastąp wartości HOST i MASTER_KEY wartościami URI i PRIMARY KEY dla konta usługi Azure Cosmos DB, a następnie zapisz plik. Aby uzyskać więcej informacji, zobacz Krok 1. Utwórz konto bazy danych usługi Azure Cosmos DB.

  12. W Eksploratorze projektów kliknij prawym przyciskiem myszy przykład azure-cosmos-java-sample, wybierz pozycję Ścieżka kompilacji, a następnie wybierz pozycję Konfiguruj ścieżkę kompilacji.

  13. Na ekranie Ścieżka kompilacji języka Java w okienku po prawej stronie wybierz kartę Biblioteki , a następnie wybierz pozycję Dodaj zewnętrzne jednostki JAR. Przejdź do lokalizacji pliku lombok.jar, a następnie wybierz pozycję Otwórz, a następnie wybierz przycisk OK.

  14. Użyj kroku 12, aby ponownie otworzyć okno Właściwości , a następnie w okienku po lewej stronie wybierz pozycję Docelowe środowiska uruchomieniowe.

  15. Na ekranie Docelowe środowiska uruchomieniowe wybierz pozycję Nowy, wybierz pozycję Apache Tomcat w wersji 7.0, a następnie wybierz przycisk OK.

  16. Użyj kroku 12, aby ponownie otworzyć okno Właściwości , a następnie w okienku po lewej stronie wybierz pozycję Aspekty projektu.

  17. Na ekranie Project Facets (Aspekty projektu ) wybierz pozycję Dynamic Web Module (Dynamiczny moduł internetowy ) i Java (Java), a następnie wybierz przycisk OK.

  18. Na karcie Serwery w dolnej części ekranu kliknij prawym przyciskiem myszy pozycję Tomcat v7.0 Server na hoście lokalnym , a następnie wybierz pozycję Dodaj i usuń.

  19. W oknie Dodawanie i usuwanie przenieś pozycję azure-cosmos-java-sample do pola Skonfigurowane , a następnie wybierz pozycję Zakończ.

  20. Na karcie Serwery kliknij prawym przyciskiem myszy pozycję Tomcat v7.0 Server at localhost, a następnie wybierz pozycję Uruchom ponownie.

  21. W przeglądarce przejdź do http://localhost:8080/azure-cosmos-java-sample/ i zacznij dodawać zadania do listy zadań. Należy pamiętać, że jeśli zmieniono domyślne wartości portów, zmień 8080 na wybraną wartość.

  22. Aby wdrożyć projekt w witrynie internetowej platformy Azure, zobacz Krok 6. Wdrażanie aplikacji w witrynach sieci Web platformy Azure.

Następne kroki

Próbujesz zaplanować pojemność na potrzeby migracji do usługi Azure Cosmos DB? Informacje o istniejącym klastrze bazy danych można użyć do planowania pojemności.