Sdílet prostřednictvím


Kurz: Vytvoření webové aplikace v Javě pomocí 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 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 vám poskytne návod k procesu instalace v části Rychlý start v kurzu: Instalace TomCat7 a použití s článkem 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 webu Azure Portal nebo na domovské stránce vyberte Vytvořit prostředek.

  2. Vyhledejte Službu Azure Cosmos DB. Vyberte Vytvořit>službu 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
    • Table
    • 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 novoua zadejte jedinečný název nové skupiny prostředků.
    Název účtu Jedinečný název Zadejte název pro identifikaci účtu služby Azure Cosmos DB. Vzhledem k tomu, že se váš identifikátor URI vytvoří připojením řetězce documents.azure.com k názvu, který zadáte, 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 vašim uživatelům nejbližší, abyste jim zajistili nejrychlejší přístup k datům.
    Režim kapacity Zřízená propustnost nebo bezserverová Vyberte Zřízenou propustnost a vytvořte účet v režimu zřízené propustnosti. Vyberte Bezserverový účet a vytvořte účet v bezserverovém režimu.
    Uplatnění slevy na úroveň Free služby Azure Cosmos DB Použít nebo nepoužovat S úrovní Free služby Azure Cosmos DB získáte prvních 1 000 RU/s a 25 GB úložiště zdarma v účtu. Další informace o úrovni Free
    Omezení celkové propustnosti účtu Vybráno nebo ne Omezte celkovou propustnost, kterou je možné zřídit pro tento účet. Tento limit zabraňuje neočekávaným poplatkům souvisejícím se zřízenou propustností. Tento limit můžete po vytvoření účtu 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 se musíte přihlásit. Pokud nevidíte možnost použít slevu na úroveň Free, je už u úrovně Free povolen jiný účet v předplatném.

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

    Poznámka:

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

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

    Nastavení Hodnota Popis
    Geografická redundance Zakázat Povolte nebo zakažte globální distribuci vašeho účtu spárováním oblasti s párovou oblastí. Další oblasti můžete do svého účtu přidat později.
    Zápisy do více oblastí Zakázat Funkce zápisu do více oblastí umožňuje využívat zřízenou propustnost pro databáze a kontejnery po celém světě.
    Zóny dostupnosti Zakázat Zóny dostupnosti vám pomohou 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:

    • Sítě. Nakonfigurujte přístup z virtuální sítě.
    • Zásady zálohování. Nakonfigurujte zásady pravidelného nebo průběžného zálohování.
    • Šifrování. Použijte klíč spravovaný službou nebo klíč spravovaný zákazníkem.
    • Značky. Značky jsou dvojice název-hodnota, které umožňují kategorizovat prostředky a zobrazovat souhrnnou fakturaci. Stačí k tomu u několika prostředků a skupin prostředků použít stejnou značku.
  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 portálu zobrazí stránka s textem Vaše nasazení je hotové.

    Snímek obrazovky ukazuje, že vaše nasazení je hotové.

  9. Pokud chcete přejít na stránku účtu Azure Cosmos DB, vyberte Přejít k prostředku.

    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 dále.

Snímek obrazovky webu 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 se dynamický webový projekt nezobrazuje jako dostupný projekt, udělejte toto: 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 Cílové modul runtime volitelně vyberte hodnotu (např. Apache Tomcat v7.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 myši 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 vyberte pro účely tohoto kurzu nový soubor JSP (html) a pak vyberte Dokončit.

  6. Když se soubor index.jsp otevře v Eclipse, přidejte text pro zobrazení Hello World! v existujícím <body> prvku. 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 Project 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 projekt převést na projekt Maven pomocí následujících kroků:

  1. V Průzkumníku projektů klikněte pravým tlačítkem na projekt, vyberte Konfigurovat, 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 do souboru pom.xml přidat kód XML závislosti pro ID skupiny a ID artefaktu:

    <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ží pom.xml soubor.

Použití služby Azure Cosmos DB v aplikaci v Javě

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

Přidat model

Nejprve nadefinujme model v rámci nového 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 objektu DAO (Data Access Object)

Vytvořte objekt pro přístup k datům (DAO), který abstrahuje zachování položek toDo ve službě 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 vytvoření nového klienta pro každý další požadavek. Klienta můžete znovu použít tak, že ho cosmosClientFactory definujete v rámci třídy. Aktualizujte hodnoty HOST a MASTER_KEY, které jste uložili v kroku 1. Nahraďte proměnnou HOST identifikátorem URI a nahraďte MASTER_KEY 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 třídu DocDbDao . Tato třída definuje kód pro zachování todoItems do kontejneru, načte databázi a kolekci, pokud existuje, nebo vytvoří nový, pokud neexistuje. Tento příklad používá Gson k serializaci a de-serializaci TodoItem Plain Old Java Objects (POJOs) 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, která načte dokumenty jiným atributem (např. ID) a nikoli vlastním propojením. Pomocnou metodu můžete použít k načtení dokumentu JSON TodoItem podle ID a následné deserializaci do POJO.

    Objekt klienta můžete také použít cosmosClient 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 ukazuje 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 vygenerovat integrované vývojové prostředí (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);
    }
}

Vytvoření servletu

Dále vytvořte servlet pro směrování požadavků HTTP na kontroler. Vytvořte soubor ApiServlet.java a definujte pod 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);
    }
}

Spojení zbytku aplikace v Javě

Teď, když jsme dokončili zábavné bity, je vše, co zbývá, 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 napsat 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ý propojí webové uživatelské rozhraní a servlet dohromady:

    /**
     * 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, zda je dokončena, přepnutím zaškrtávacího políčka a kliknutím 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ého projektu 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 webových aplikací 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 Soubor vyberte 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 Zdrojové úložiště Git zadejte https://github.com/Azure-Samples/azure-cosmos-java-sql-api-todo-appdo pole identifikátoru URI a pak vyberte Další.

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

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

  9. Na obrazovce Vybrat průvodce, který chcete použít pro import projektů, ujistěte se, že je vybrána 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 a nahraďte hodnoty HOST a MASTER_KEY identifikátorem URI a PRIMÁRNÍM KLÍČEM pro úč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 Průzkumníku projektů klikněte pravým tlačítkem na ukázku azure-cosmos-java-sample, vyberte Cestu sestavení a pak vyberte Konfigurovat cestu sestavení.

  13. Na obrazovce Cesta sestavení Java v pravém podokně vyberte kartu Knihovny a pak vyberte Přidat externí žádosti o přijetí změn. Přejděte do umístění lombok.jar souboru a 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 Cílové moduly runtime vyberte Nový, vyberte Apache Tomcat v7.0 a pak vyberte OK.

  16. Pomocí kroku 12 znovu otevřete okno Vlastnosti a v levém podokně vyberte Omezující vlastnosti projektu.

  17. Na obrazovce Project Facets (Omezující vlastnosti projektu) vyberte Dynamic Web Module (Dynamický webový modul) a Java (Java) a pak vyberte OK.

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

  19. V okně Přidat a odebrat přesuňte azure-cosmos-java-sample do pole Nakonfigurované a pak vyberte Dokončit.

  20. Na kartě Servery klepněte pravým tlačítkem myši Tomcat v7.0 Server na localhost a pak vyberte 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. Pokud chcete projekt nasadit na web Azure, přečtěte si krok 6. Nasaďte aplikaci na weby Azure.

Další kroky

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