Kurz: Sestavení webové aplikace v Javě s využitím služby Azure Cosmos DB a rozhraní API pro NoSQL

PLATÍ PRO: NoSQL

Tento kurz vývoje webové aplikace Java ukazuje, jak pomocí služby Microsoft Azure Cosmos DB ukládat data a přistupovat k nim z aplikace Java hostované ve službě Azure App Service Web Apps. Bez platební karty nebo předplatného Azure si můžete nastavit bezplatný účet Vyzkoušet službu Azure Cosmos DB. V tomto článku se dozvíte:

  • Jak vytvořit základní aplikaci JSP (JavaServer Pages) v prostředí Eclipse.
  • Jak pracovat se službou Azure Cosmos DB pomocí sady Azure Cosmos DB Java SDK.

Tento kurz o aplikaci Java vám ukáže, jak vytvořit webovou aplikaci pro správu úkolů, která umožňuje vytvářet a získávat úkoly a označovat je jako dokončené, jak ilustruje následující obrázek. Každý z úkolů v seznamu se ve službě Azure Cosmos DB ukládá jako dokument JSON.

Aplikace pro seznam úkolů v jazyce Java

Tip

V tomto kurzu vývoje aplikace se předpokládá, že již máte zkušenosti s jazykem Java. Pokud je pro vás Java nebo některý z požadovaných nástrojů nový, doporučujeme stáhnout úplný ukázkový projekt todo z GitHubu a postupovat podle pokynů na konci tohoto článku. Až jej budete mít sestavený, můžete se k tomuto článku vrátit, abyste kódu lépe porozuměli v kontextu projektu.

Předpoklady pro tento kurz webové aplikace Java

Než zahájíte tento kurz vývoje aplikace, musíte mít následující:

Pokud tyto nástroje instalujete poprvé, coreservlets.com poskytuje návod k procesu instalace v části Rychlý start v článku Kurz: Instalace TomCat7 a jeho použití s Eclipse .

Vytvoření účtu služby Azure Cosmos DB

Začněme vytvořením účtu služby Azure Cosmos DB. Pokud již účet máte nebo pokud používáte pro účely tohoto kurzu emulátor služby Azure Cosmos DB, můžete přeskočit na Krok 2: Vytvoření aplikace Java JSP.

  1. V nabídce Azure Portal nebo na domovské stránce vyberte Vytvořit prostředek.

  2. Vyhledejte Azure Cosmos DB. Vyberte Vytvořit>Azure Cosmos DB.

  3. Na stránce Vytvořit účet služby Azure Cosmos DB vyberte možnost Vytvořit v části Azure Cosmos DB for NoSQL .

    Azure Cosmos DB poskytuje několik rozhraní API:

    • NoSQL pro data dokumentů
    • PostgreSQL
    • MongoDB – pro data dokumentů
    • Apache Cassandra
    • Tabulka
    • Apache Gremlin pro data grafu

    Další informace o rozhraní API pro NoSQL najdete v tématu Vítá vás Azure Cosmos DB.

  4. Na stránce Vytvořit účet služby Azure Cosmos DB zadejte základní nastavení nového účtu služby Azure Cosmos DB.

    Nastavení Hodnota Popis
    Předplatné Název předplatného Vyberte předplatné Azure, které chcete pro tento účet služby Azure Cosmos DB použít.
    Skupina prostředků Název skupiny prostředků Vyberte skupinu prostředků nebo vyberte Vytvořit novou a pak zadejte jedinečný název nové skupiny prostředků.
    Account Name Jedinečný název Zadejte název pro identifikaci účtu služby Azure Cosmos DB. Vzhledem k tomu , že documents.azure.com je připojen k názvu, který zadáte k vytvoření identifikátoru URI, použijte jedinečný název. Název může obsahovat pouze malá písmena, číslice a znak spojovníku (-). Musí mít 3 až 44 znaků.
    Umístění Oblast nejbližší vašim uživatelům Vyberte zeměpisné umístění, ve kterém chcete účet služby Azure Cosmos DB hostovat. Použijte umístění, které je nejblíže vašim uživatelům, abyste jim poskytli nejrychlejší přístup k datům.
    Režim kapacity Zřízená propustnost nebo bezserverová Vyberte Zřízená propustnost a vytvořte účet v režimu zřízené propustnosti . Výběrem možnosti Bezserverový vytvořte účet v bezserverovém režimu.
    Uplatnění slevy na úrovni Free služby Azure Cosmos DB Použít nebo Neaplikovat S úrovní Free služby Azure Cosmos DB získáte v účtu zdarma prvních 1 000 RU/s a 25 GB úložiště. Přečtěte si další informace o úrovni Free.
    Omezení celkové propustnosti účtu Vybráno nebo ne Omezte celkovou propustnost, kterou je možné pro tento účet zřídit. Tento limit zabraňuje neočekávaným poplatkům souvisejícím se zřízenou propustností. Po vytvoření účtu můžete tento limit aktualizovat nebo odebrat.

    Pro každé předplatné Azure můžete mít až jeden účet služby Azure Cosmos DB úrovně Free a při vytváření účtu musíte vyjádřit výslovný souhlas. Pokud možnost uplatnění slevy na úrovni Free nevidíte, už má jiný účet v předplatném povolenou úroveň Free.

    Snímek obrazovky se stránkou Vytvoření účtu služby Azure Cosmos DB

    Poznámka

    Následující možnosti nejsou k dispozici, pokud jako režim kapacity vyberete bezserverový režim:

    • Použít slevu založenou na bezplatné úrovni
    • Omezení celkové propustnosti účtu
  5. Na kartě Globální distribuce nakonfigurujte následující podrobnosti. Pro tento rychlý start můžete ponechat výchozí hodnoty:

    Nastavení Hodnota Popis
    Geografická redundance Zakázat Povolte nebo zakažte globální distribuci ve svém účtu spárováním oblasti s párovací oblastí. Později můžete ke svému účtu přidat další oblasti.
    Zápisy do více oblastí Zakázat Funkce zápisu do více oblastí umožňuje využívat zřízenou propustnost pro vaše databáze a kontejnery po celém světě.
    Zóny dostupnosti Zakázat Zóny dostupnosti vám pomůžou dále zlepšit dostupnost a odolnost vaší aplikace.

    Poznámka

    Následující možnosti nejsou k dispozici, pokud jako režim kapacity na předchozí stránce Základy vyberete bezserverový režim:

    • Geografická redundance
    • Zápisy do více oblastí
  6. Volitelně můžete nakonfigurovat další podrobnosti na následujících kartách:

  7. Vyberte Zkontrolovat a vytvořit.

  8. Zkontrolujte nastavení účtu a pak vyberte Vytvořit. Vytvoření účtu trvá několik minut. Počkejte, až se na stránce portálu zobrazí Vaše nasazení je dokončené.

    Snímek obrazovky ukazuje, že vaše nasazení je dokončené.

  9. Výběrem možnosti Přejít k prostředku přejděte na stránku účtu služby Azure Cosmos DB.

    Snímek obrazovky se stránkou účtu služby Azure Cosmos DB

Přejděte na stránku účtu služby Azure Cosmos DB a vyberte Klíče. Zkopírujte hodnoty, které chcete použít ve webové aplikaci, kterou vytvoříte jako další.

Snímek obrazovky Azure Portal se zvýrazněným tlačítkem Klíče na stránce účtu služby Azure Cosmos DB

Vytvoření aplikace Java JSP

Vytvoření aplikace JSP:

  1. Nejprve začneme vytvořením projektu v Javě. Spusťte Eclipse, pak vyberte Soubor, vyberte Nový a pak vyberte Dynamický webový projekt. Pokud nevidíte dynamický webový projekt uvedený jako dostupný projekt, postupujte takto: Vyberte Soubor, vyberte Nový, vyberte Projekt..., rozbalte Web, vyberte Dynamický webový projekt a vyberte Další.

    Vývoj aplikace Java JSP

  2. Do pole Název projektu zadejte název projektu a v rozevírací nabídce Target Runtime volitelně vyberte hodnotu (např. Apache Tomcat verze 7.0) a pak vyberte Dokončit. Pokud vyberete cílový modul runtime, budete moci spouštět projekt místně přes Eclipse.

  3. V prostředí Eclipse v zobrazení Project Explorer (Průzkumník projektů) rozbalte projekt. Klikněte pravým tlačítkem na WebContent, vyberte Nový a pak vyberte Soubor JSP.

  4. V dialogovém okně New JSP File (Nový soubor JSP) pojmenujte soubor index.jsp. Ponechte nadřazenou složku jako WebContent, jak je znázorněno na následujícím obrázku, a pak vyberte Další.

    Vytvoření nového souboru JSP – kurz vývoje aplikace Java

  5. V dialogovém okně Vybrat šablonu JSP pro účely tohoto kurzu vyberte Nový soubor JSP (html) a pak vyberte Dokončit.

  6. Když se soubor index.jsp otevře v Eclipse, přidejte text, který zobrazí Hello World! v existujícím <body> elementu. Aktualizovaný obsah <body> by se měl podobat následujícímu kódu:

    <body>
      <% out.println("Hello World!"); %>
    </body>
    
  7. Uložte soubor index.jsp .

  8. Pokud nastavíte cílový modul runtime v kroku 2, můžete vybrat Projekt a pak spustit aplikaci JSP místně:

    Hello World – kurz aplikace Java

Instalace sady SQL Java SDK

Nejjednodušším způsobem, jak stáhnout sadu SQL Java SDK a její závislosti, je použít Apache Maven. K tomu je potřeba převést projekt na projekt Maven pomocí následujícího postupu:

  1. Klikněte pravým tlačítkem na projekt v Průzkumníku projektů, vyberte Konfigurovat a vyberte Převést na projekt Maven.

  2. V okně Vytvořit nový POM přijměte výchozí hodnoty a vyberte Dokončit.

  3. V Project Exploreru otevřete soubor pom.xml.

  4. Na kartě Závislosti v podokně Závislosti vyberte Přidat.

  5. V okně Select Dependency (Vybrat závislost) udělejte následující:

    • Do pole ID skupiny zadejte com.azure.
    • Do pole Id artefaktu zadejte azure-cosmos.
    • Do pole Verze zadejte 4.11.0.

    Nebo můžete přidat xml závislostí pro ID skupiny a ID artefaktu přímo do souborupom.xml :

    <dependency>
      <groupId>com.azure</groupId>
      <artifactId>azure-cosmos</artifactId>
      <version>4.11.0</version>
    </dependency>
    
  6. Vyberte OK a Maven nainstaluje sadu SQL Java SDK nebo uloží soubor pom.xml.

Použití služby Azure Cosmos DB v aplikaci Java

Teď přidáme modely, zobrazení a kontrolery do vaší webové aplikace.

Přidání modelu

Nejprve definujme model v novém souboru TodoItem.java. Třída TodoItem definuje schéma položky spolu s metodami getter a 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;
    }
    
    
}

Přidání tříd DAO (Data Access Object)

Vytvořte objekt DAO (Data Access Object), který abstrahuje uchování položek úkolů do služby Azure Cosmos DB. Abychom mohli položky ToDo ukládat do kolekce, klient musí vědět, která databáze nebo kolekce se má k uchovávání použít (podle odkazů na sebe sama). Obecně je nejlépe uložit databázi a kolekci do mezipaměti, kdykoli je to možné, aby se zamezilo nadbytečným přístupům do databáze.

  1. Pokud chcete vyvolat službu Azure Cosmos DB, musíte vytvořit instanci nového cosmosClient objektu. Obecně platí, že je nejlepší objekt znovu použít cosmosClient místo vytváření nového klienta pro každý další požadavek. Klienta můžete znovu použít tak, že ho definujete v cosmosClientFactory rámci třídy. Aktualizujte hodnoty HOST a MASTER_KEY, které jste uložili v kroku 1. Proměnnou HOST nahraďte identifikátorem URI a MASTER_KEY nahraďte primárním klíčem. Pomocí následujícího kódu vytvořte CosmosClientFactory třídu v souboru 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. Vytvořte nový soubor TodoDao.java a přidejte TodoDao třídu pro vytvoření, aktualizaci, čtení a odstranění položek úkolů:

    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. Vytvořte nový soubor MockDao.java a přidejte MockDao třídu. Tato třída implementuje TodoDao třídu pro provádění operací CRUD s položkami:

    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. Vytvořte nový soubor DocDbDao.java a přidejte DocDbDao třídu. Tato třída definuje kód pro zachování todoItems do kontejneru, načte databázi a kolekci, pokud existuje, nebo vytvoří novou, pokud neexistuje. Tento příklad používá Gson k serializaci a de-serializaci TodoItem Plain Old Java Objects (POJO) do dokumentů JSON. Abychom mohli položky ToDo ukládat do kolekce, klient musí vědět, která databáze nebo kolekce se má k uchovávání použít (podle odkazů na sebe sama). Tato třída také definuje pomocnou funkci pro načtení dokumentů pomocí jiného atributu (např. "ID") místo samoobslužného propojení. Pomocí pomocné metody můžete načíst dokument JSON TodoItem podle ID a pak ho deserializovat na POJO.

    Můžete také použít cosmosClient objekt klienta k získání kolekce nebo seznamu TodoItems pomocí dotazu SQL. Nakonec definujete metodu delete pro odstranění TodoItem ze seznamu. Následující kód zobrazuje obsah DocDbDao třídy:

    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. Dále vytvořte nový soubor TodoDaoFactory.java a přidejte TodoDaoFactory třídu, která vytvoří nový objekt DocDbDao:

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

Přidání kontroleru

Přidejte do aplikace kontroler TodoItemController . V tomto projektu používáte Project Lombok, pomocí kterého generujeme konstruktor, metody getter a setter a tvůrce (builder). Případně můžete tento kód napsat ručně nebo ho nechat integrované vývojové prostředí (IDE) vygenerovat:

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

Vytvoření servletu

Dále vytvořte servlet pro směrování požadavků HTTP do kontroleru. Vytvořte soubor ApiServlet.java a definujte v něm následující kód:

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

Propojení zbytku aplikace v Javě

Teď, když jsme dokončili zábavné kousky, zbývá jen vytvořit rychlé uživatelské rozhraní a připojit ho k vašemu DAO.

  1. K zobrazení uživateli potřebujete webové uživatelské rozhraní. Pojďme znovu zapsat index.jsp , který jsme vytvořili dříve, pomocí následujícího kódu:

    <%@ 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. Nakonec napište javascript na straně klienta, který spojí webové uživatelské rozhraní a servlet:

    /**
     * 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. Nyní již zbývá aplikaci jen otestovat. Spusťte aplikaci místně a zadáním názvů a kategorie položek a kliknutím na Add Task (Přidat úkol) přidejte několik položek Todo. Po zobrazení položky můžete aktualizovat, jestli je dokončená, a to tak, že přepnete zaškrtávací políčko a kliknete na Aktualizovat úkoly.

Nasazení aplikace v Javě na weby Azure

Díky Azure Websites je nasazování aplikací Java stejně snadné jako export aplikace jako souboru WAR a jeho nahrání buď přes správu zdrojových kódů (např. Git), nebo FTP.

  1. Pokud chcete aplikaci exportovat jako soubor WAR, klikněte pravým tlačítkem na projekt v Průzkumníku projektů, vyberte Exportovat a pak vyberte Soubor WAR.

  2. V okně WAR Export udělejte následující:

    • Do pole Webový projekt zadejte azure-cosmos-java-sample.
    • V poli Destination (Cíl) vyberte cíl, do kterého se uloží soubor WAR.
    • Vyberte Dokončit.
  3. Nyní když máte k dispozici soubor WAR, můžete tento soubor jednoduše nahrát do adresáře webapps Azure Websites. Pokyny, jak soubor nahrát, najdete v tématu o přidání aplikace Java do Azure App Service Web Apps. Po nahrání souboru WAR do adresáře webapps prostředí runtime zjistí, že jste ho přidali, a automaticky ho načte.

  4. Chcete-li zobrazit hotový produkt, přejděte na http://YOUR\_SITE\_NAME.azurewebsites.net/azure-cosmos-java-sample/ a začněte přidávat úkoly.

Získání projektu z Githubu

Všechny ukázky v tomto kurzu jsou součástí projektu todo na GitHubu. Pokud chcete importovat projekt todo do prostředí Eclipse, ujistěte se, že máte software a prostředky uvedené v části Předpoklady, a udělejte následující:

  1. Nainstalujte Project Lombok. Lombok slouží ke generování konstruktorů a metod getter a setter v projektu. Jakmile budete mít stažen soubor lombok.jar, dvakrát na něj klikněte, aby se nainstaloval, nebo jej nainstalujte z příkazového řádku.

  2. Pokud je prostředí Eclipse otevřené, zavřete ho a znovu ho spusťte, aby se načetl Lombok.

  3. V Eclipse v nabídce File (Soubor ) vyberte Import (Importovat).

  4. V okně Import vyberte Git, vyberte Projekty z Gitu a pak vyberte Další.

  5. Na obrazovce Select Repository Source (Vybrat zdroj úložiště ) vyberte Clone URI (Klonovat identifikátor URI).

  6. Na obrazovce Source Git Repository (Zdrojové úložiště Git) zadejte https://github.com/Azure-Samples/azure-cosmos-java-sql-api-todo-appdo pole URI text a pak vyberte Next (Další).

  7. Na obrazovce Výběr větve se ujistěte, že je vybraná možnost main , a pak vyberte Další.

  8. Na obrazovce Místní cíl vyberte Procházet a vyberte složku, do které se dá úložiště zkopírovat, a pak vyberte Další.

  9. Na obrazovce Vyberte průvodce, který se má použít pro import projektů zkontrolujte, že je vybraná možnost Importovat existující projekty , a pak vyberte Další.

  10. Na obrazovce Importovat projekty zrušte výběr projektu DocumentDB a pak vyberte Dokončit. Projekt Azure Cosmos DB obsahuje sadu Azure Cosmos DB Java SDK, kterou přidáme jako závislost.

  11. V Project Exploreru přejděte na azure-cosmos-java-sample\src\com.microsoft.azure.cosmos.sample.dao\DocumentClientFactory.java, nahraďte hodnoty HOST a MASTER_KEY identifikátorem URI a PRIMÁRNÍM KLÍČEM pro váš účet služby Azure Cosmos DB a pak soubor uložte. Další informace najdete v kroku 1. Vytvořte účet databáze Azure Cosmos DB.

  12. V Project Exploreru klikněte pravým tlačítkem na azure-cosmos-java-sample, vyberte Build Path (Cesta sestavení) a pak vyberte Configure Build Path (Konfigurovat cestu sestavení).

  13. Na obrazovce Java Build Path (Cesta sestavení Java) v pravém podokně vyberte kartu Libraries (Knihovny) a pak vyberte Add External JARs (Přidat externí jary). Přejděte do umístění souboru lombok.jar, vyberte Otevřít a pak vyberte OK.

  14. Pomocí kroku 12 znovu otevřete okno Vlastnosti a pak v levém podokně vyberte Cílové moduly runtime.

  15. Na obrazovce Targeted Runtimes (Cílové moduly runtime ) vyberte New (Nový), vyberte Apache Tomcat v7.0 a pak vyberte OK.

  16. Pomocí kroku 12 znovu otevřete okno Vlastnosti a pak v levém podokně vyberte Project Facets.

  17. Na obrazovce Project Facets vyberte Dynamický webový modul a Java a pak vyberte OK.

  18. Na kartě Servers (Servery ) v dolní části obrazovky klikněte pravým tlačítkem na Tomcat v7.0 Server at localhost a pak vyberte Add and Remove (Přidat a odebrat).

  19. V okně Přidat a odebrat přesuňteazure-cosmos-java-sample do pole Nakonfigurováno a pak vyberte Dokončit.

  20. Na kartě Servers (Servery ) klikněte pravým tlačítkem na Tomcat v7.0 Server at localhost a pak vyberte Restart (Restartovat).

  21. V prohlížeči přejděte na adresu http://localhost:8080/azure-cosmos-java-sample/ a začněte přidávat položky do seznamu úkolů. Poznámka: Pokud jste změnili výchozí hodnoty portů, změňte 8080 na hodnotu, kterou jste si vybrali.

  22. Nasazení projektu na web Azure najdete v kroku 6. Nasaďte aplikaci na weby Azure.

Další kroky

Pokoušíte se naplánovat kapacitu pro migraci do služby Azure Cosmos DB? Informace o existujícím databázovém clusteru můžete použít k plánování kapacity.