Självstudie: Skapa en Java-webbapp med Hjälp av Azure Cosmos DB och API:et för NoSQL

GÄLLER FÖR: NoSQL

Den här självstudiekursen om webbprogram i Java beskriver hur du kan använda tjänsten Microsoft Azure Cosmos DB för att lagra och komma åt data från ett Java-program i Azure App Service Web Apps. Utan ett kreditkort eller en Azure-prenumeration kan du konfigurera ett kostnadsfritt Prova Azure Cosmos DB-konto. I den här artikeln lär du dig att:

  • Skapa ett enkelt JSP-program (JavaServer Pages) i Eclipse.
  • Arbeta med Azure Cosmos DB-tjänsten med hjälp av Azure Cosmos DB Java SDK.

I den här självstudien om Java visar vi hur du skapar en webbaserad aktivitetshanteringsapp där du kan skapa, hämta och markera aktiviteter som slutförda, enligt bilden nedan. Alla aktiviteter i att göra-listan lagras som JSON-dokument i Azure Cosmos DB.

Java-app med att göra-lista

Tips

Den här självstudien om apputveckling förutsätter att du har tidigare erfarenhet av Java. Om du inte har använt Java eller verktygen som krävs tidigare, rekommenderar vi att du hämtar det fullständiga todo-projektet från GitHub och skapar det enligt anvisningarna i slutet av artikeln. När du har skapat det kan du läsa den här artikeln för information om koden i projektets sammanhang.

Förutsättningar för självstudien om Java-webbappen

Innan du påbörjar den här självstudien om apputveckling måste du ha följande:

Om du installerar de här verktygen för första gången ger coreservlets.com en genomgång av installationsprocessen i snabbstartsavsnittet i artikeln Självstudie: Installera TomCat7 och Använda det med Eclipse .

Skapa ett Azure Cosmos DB-konto

Vi ska börja med att skapa ett Azure Cosmos DB-konto. Om du redan har ett konto eller om du använder Azure Cosmos DB-emulatorn för den här kursen kan du gå vidare till Steg 2: Skapa Java JSP-programmet.

  1. Välj Skapa en resurs på Azure Portal-menyn eller på startsidan.

  2. Sök efter Azure Cosmos DB. Välj Skapa>Azure Cosmos DB.

  3. På sidan Skapa ett Azure Cosmos DB-konto väljer du alternativet Skapa i avsnittet Azure Cosmos DB för NoSQL .

    Azure Cosmos DB innehåller flera API:er:

    • NoSQL för dokumentdata
    • PostgreSQL
    • MongoDB, för dokumentdata
    • Apache Cassandra
    • Tabell
    • Apache Gremlin, för grafdata

    Mer information om API:et för NoSQL finns i Välkommen till Azure Cosmos DB.

  4. På sidan Skapa Azure Cosmos DB-konto anger du de grundläggande inställningarna för det nya Azure Cosmos DB-kontot.

    Inställningen Värde Beskrivning
    Prenumeration Prenumerationens namn Välj den Azure-prenumeration som ska användas för det här Azure Cosmos DB-kontot.
    Resursgrupp Namn på resursgrupp Välj en resursgrupp eller välj Skapa ny och ange sedan ett unikt namn för den nya resursgruppen.
    Account Name Ett unikt namn Ange ett namn för att identifiera ditt Azure Cosmos DB-konto. Eftersomdocuments.azure.com läggs till det namn du anger för att skapa din URI måste du använda ett unikt namn. Namnet får bara innehålla gemener, siffror och bindestreck (-). Det måste vara 3–44 tecken.
    Location Den region som är närmast dina användare Välj en geografisk plats som värd för ditt Azure Cosmos DB-konto. Använd den plats som är närmast dina användare för att ge dem så snabb åtkomst till data som möjligt.
    Kapacitetsläge Etablerat dataflöde eller serverlöst Välj Etablerat dataflöde för att skapa ett konto i etablerat dataflödesläge . Välj Serverlös för att skapa ett konto i serverlöst läge.
    Tillämpa rabatt på den kostnadsfria Azure Cosmos DB-nivån Använd eller använd inte Med den kostnadsfria Azure Cosmos DB-nivån får du de första 1 000 RU/s och 25 GB lagringsutrymme kostnadsfritt på ett konto. Läs mer om den kostnadsfria nivån.
    Begränsa det totala kontots dataflöde Markerad eller inte Begränsa den totala mängden dataflöde som kan etableras för det här kontot. Den här gränsen förhindrar oväntade avgifter relaterade till etablerat dataflöde. Du kan uppdatera eller ta bort den här gränsen när ditt konto har skapats.

    Du kan ha upp till ett Azure Cosmos DB-konto på kostnadsfri nivå per Azure-prenumeration och måste välja när du skapar kontot. Om du inte ser alternativet för att tillämpa rabatten på den kostnadsfria nivån har ett annat konto i prenumerationen redan aktiverats med den kostnadsfria nivån.

    Skärmbild som visar sidan Skapa Azure Cosmos DB-konto.

    Anteckning

    Följande alternativ är inte tillgängliga om du väljer Serverlös som kapacitetsläge:

    • Tillämpa rabatt för kostnadsfri nivå
    • Begränsa det totala kontots dataflöde
  5. På fliken Global distribution konfigurerar du följande information. Du kan lämna standardvärdena för den här snabbstarten:

    Inställningen Värde Beskrivning
    Geo-redundans Inaktivera Aktivera eller inaktivera global distribution för ditt konto genom att para ihop din region med en parregion. Du kan lägga till fler regioner i ditt konto senare.
    Skrivåtgärder för flera regioner Inaktivera Med funktionen skrivningar i flera regioner kan du dra nytta av det etablerade dataflödet för dina databaser och containrar över hela världen.
    Tillgänglighetszoner Inaktivera Tillgänglighetszoner hjälper dig att ytterligare förbättra programmets tillgänglighet och återhämtning.

    Anteckning

    Följande alternativ är inte tillgängliga om du väljer Serverlös som kapacitetsläge på föregående grundläggande sida:

    • Geo-redundans
    • Skrivåtgärder för flera regioner
  6. Du kan också konfigurera mer information på följande flikar:

    • Nätverk. Konfigurera åtkomst från ett virtuellt nätverk.
    • Säkerhetskopieringsprincip. Konfigurera antingen regelbunden eller kontinuerlig säkerhetskopieringsprincip.
    • Kryptering. Använd antingen en tjänsthanterad nyckel eller en kundhanterad nyckel.
    • Taggar. Taggar är namn/värde-par som gör att du kan kategorisera resurser och visa konsoliderad fakturering genom att använda samma tagg för flera resurser och resursgrupper.
  7. Välj Granska + skapa.

  8. Granska kontoinställningarna och välj sedan Skapa. Det tar några minuter att skapa kontot. Vänta tills portalsidan visar meddelandet Distributionen är klar.

    Skärmbild som visar att distributionen är klar.

  9. Välj Gå till resurs för att gå till sidan för Azure Cosmos DB-kontot.

    Skärmbild som visar kontosidan för Azure Cosmos DB.

Gå till azure Cosmos DB-kontosidan och välj Nycklar. Kopiera de värden som ska användas i det webbprogram som du skapar härnäst.

Skärmbild av Azure Portal med knappen Nycklar markerad på Azure Cosmos DB-kontosidan

Skapa Java JSP-programmet

Så här skapar du JSP-appen:

  1. Först börjar vi med att skapa ett Java-projekt. Starta Eclipse, välj sedan Arkiv, välj Nytt och välj sedan Dynamiskt webbprojekt. Om det dynamiska webbprojektet inte visas som ett tillgängligt projekt gör du följande: Välj Fil, välj Nytt, välj Projekt..., expandera Webb, välj Dynamiskt webbprojekt och välj Nästa.

    JSP Java-apputveckling

  2. Ange ett projektnamn i rutan Projektnamn och i listrutan Målkörning väljer du ett värde (t.ex. Apache Tomcat v7.0) och väljer sedan Slutför. När du väljer ett mål för körning kan du köra projektet lokalt genom Eclipse.

  3. Expandera projektet i Eclipse i vyn Projektutforskaren. Högerklicka på WebContent, välj Ny och välj sedan JSP-fil.

  4. I dialogrutan Ny JSP-fil namnger du filen index.jsp. Behåll den överordnade mappen som WebContent, som du ser i följande bild, och välj sedan Nästa.

    Skapa en ny JSP-fil – självstudie om Java-webbapp

  5. I dialogrutan Välj JSP-mall väljer du Ny JSP-fil (html) i den här självstudien och väljer sedan Slutför.

  6. När filen index.jsp öppnas i Eclipse lägger du till text för att visa Hello World! i det befintliga <body> elementet. Det uppdaterade <body>-innehållet bör likna följande kod:

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

  8. Om du anger en målkörning i steg 2 kan du välja Projekt och sedan Köra för att köra JSP-programmet lokalt:

    Hello World – självstudie om Java-app

Installera SQL Java SDK

Det enklaste sättet att hämta SQL Java SDK och dess beroenden är via Apache Maven. För att göra detta måste du konvertera projektet till ett Maven-projekt med hjälp av följande steg:

  1. Högerklicka på projektet i Projektutforskaren, välj Konfigurera, välj Konvertera till Maven-projekt.

  2. I fönstret Skapa ny POM accepterar du standardinställningarna och väljer Slutför.

  3. Öppna filen pom.xml i Projektutforskaren.

  4. På fliken Beroenden går du till fönstret Beroenden och väljer Lägg till.

  5. I fönstret Välj beroende gör du följande:

    • I rutan Grupp-ID anger du com.azure.
    • I rutan Artefakt-ID anger du azure-cosmos.
    • I rutan Version anger du 4.11.0.

    Eller så kan du lägga till beroende-XML för grupp-ID och artefakt-ID direkt i pom.xml-filen :

    <dependency>
      <groupId>com.azure</groupId>
      <artifactId>azure-cosmos</artifactId>
      <version>4.11.0</version>
    </dependency>
    
  6. Välj OK så installerar Maven SQL Java SDK eller sparar pom.xml-filen.

Använda Azure Cosmos DB-tjänsten i java-programmet

Nu ska vi lägga till modellerna, vyerna och kontrollanterna i webbappen.

Lägga till en modell

Först ska vi definiera en modell i en ny fil TodoItem.java. Klassen TodoItem definierar schemat för ett objekt tillsammans med metoderna getter och 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;
    }
    
    
}

Lägg till DAO-klasserna (Data Access Object)

Skapa ett dataåtkomstobjekt (DAO) för att abstrahera beständiga ToDo-objekt till Azure Cosmos DB. För att kunna spara ToDo-objekt till en samling måste klienten veta vilken databas och samling de ska sparas i (refererade med självlänkar). Det är vanligen bäst att cachelagra databasen och samlingen när det är möjligt, för att undvika ytterligare rundor till databasen.

  1. Om du vill anropa Azure Cosmos DB-tjänsten måste du instansiera ett nytt cosmosClient objekt. I allmänhet är det bäst att återanvända cosmosClient objektet i stället för att skapa en ny klient för varje efterföljande begäran. Du kan återanvända klienten genom att definiera den cosmosClientFactory i klassen. Uppdatera värden och MASTER_KEY värden som du sparade i steg 1. Ersätt HOST-variabeln med din URI och ersätt MASTER_KEY med primärnyckeln. Använd följande kod för att skapa CosmosClientFactory klassen i filen 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. Skapa en ny TodoDao.java-fil och lägg till TodoDao klassen för att skapa, uppdatera, läsa och ta bort att göra-objekten:

    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. Skapa en ny MockDao.java-fil och lägg till MockDao klassen. Den TodoDao här klassen implementerar klassen för att utföra CRUD-åtgärder på objekten:

    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. Skapa en ny DocDbDao.java-fil och lägg till DocDbDao klassen. Den här klassen definierar kod för att spara TodoItems i containern, hämtar databasen och samlingen, om den finns eller skapar en ny om den inte finns. I det här exemplet används Gson för att serialisera och av-serialisera TodoItem Plain Old Java-objekt (POJO) till JSON-dokument. För att kunna spara ToDo-objekt till en samling måste klienten veta vilken databas och samling de ska sparas i (refererade med självlänkar). Den här klassen definierar även hjälpfunktionen för att hämta dokumenten med ett annat attribut (t.ex. "ID") i stället för självlänk. Du kan använda hjälpmetoden för att hämta ett TodoItem JSON-dokument efter ID och sedan deserialisera det till en POJO.

    Du kan också använda klientobjektet cosmosClient för att hämta en samling eller lista över TodoItems med hjälp av en SQL-fråga. Slutligen definierar du borttagningsmetoden för att ta bort en TodoItem från listan. Följande kod visar innehållet i DocDbDao klassen:

    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. Skapa sedan en ny TodoDaoFactory.java-fil och lägg till klassen TodoDaoFactory som skapar ett nytt DocDbDao-objekt:

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

Lägga till en styrenhet

Lägg till TodoItemController-kontrollanten i ditt program. I det här projektet generar vi konstruktorn, get-metoder, set-metoder och ett verktyg via Project Lombok. Du kan också skriva den här koden manuellt eller låta IDE generera den:

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

Skapa en servlet

Skapa sedan en servlet för att dirigera HTTP-begäranden till kontrollanten. Skapa filen ApiServlet.java och definiera följande kod under den:

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

Koppla ihop resten av Java-appen

Nu när vi har slutfört de roliga bitarna är allt som återstår att skapa ett snabbt användargränssnitt och koppla det till din DAO.

  1. Du behöver ett webbanvändargränssnitt som ska visas för användaren. Nu ska vi skriva om index.jsp som vi skapade tidigare med följande kod:

    <%@ 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. Skriv slutligen javascript på klientsidan för att koppla samman webbanvändargränssnittet och servleten:

    /**
     * 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. Nu återstår bara att testa appen. Kör appen lokalt och lägg till några Todo-objekt genom att fylla i namn och kategori och klicka på Lägg till aktivitet. När objektet visas kan du uppdatera om det är klart genom att växla kryssrutan och klicka på Uppdatera uppgifter.

Distribuera Java-programmet till Azure Web Sites

Med Azure Web Sites är det enkelt att distribuera Java-appar. Allt du behöver göra är att exportera appen som en WAR-fil och antingen ladda upp den via källkontrollen (t.ex. Git) eller FTP.

  1. Om du vill exportera programmet som en WAR-fil högerklickar du på projektet i Project Explorer, väljer Exportera och väljer sedan WAR-fil.

  2. I fönstret WAR-export gör du så här:

    • I rutan Webbprojekt anger du azure-cosmos-java-sample.
    • Välj en plats där WAR-filen ska sparas i rutan Destination.
    • Välj Slutför.
  3. Nu när du har en WAR-fil laddar du bara upp den till katalogen Webbappar på Azure Web Sites. Anvisningar som beskriver hur du laddar upp filen finns i Add a Java application to Azure App Service Web Apps (Lägga till ett Java-program i Azure App Service Web Apps). När WAR-filen har laddats upp till webapps-katalogen identifierar körningsmiljön att du har lagt till den och läser in den automatiskt.

  4. Du kan visa den färdiga produkten genom att gå till http://YOUR\_SITE\_NAME.azurewebsites.net/azure-cosmos-java-sample/ och börja lägga till aktiviteter!

Hämta projektet från GitHub

Alla exempel i den här självstudien finns i projektet Todo på GitHub. Om du vill importera Todo-projektet till Eclipse ska du se till att du har de program och resurser som anges i avsnittet Förutsättningar och sedan göra följande:

  1. Installera Project Lombok. Lombok används för att generera konstruktorer, get-metoder och set-metoder i projektet. När du har laddat ned filen lombok.jar installerar du den genom att dubbelklicka på filen eller från kommandoraden.

  2. Om Eclipse är öppet startar du om det för att läsa in Lombok.

  3. I Eclipse går du till Arkiv-menyn och väljer Importera.

  4. I fönstret Importera väljer du Git, projekt från Git och sedan Nästa.

  5. På skärmen Välj lagringsplatskälla väljer du Klona URI.

  6. På skärmen Git-källlagringsplats går du till rutan URI och anger https://github.com/Azure-Samples/azure-cosmos-java-sql-api-todo-appoch väljer sedan Nästa.

  7. På skärmen Förgreningsval kontrollerar du att huvud är markerat och väljer sedan Nästa.

  8. På skärmen Lokalt mål väljer du Bläddra för att välja en mapp där lagringsplatsen kan kopieras och väljer sedan Nästa.

  9. På skärmen Välj en guide som ska användas för att importera projekt kontrollerar du att Importera befintliga projekt är markerat och väljer sedan Nästa.

  10. På skärmen Importera projekt avmarkerar du DocumentDB-projektet och väljer sedan Slutför. Azure DocumentDB-projektet innehåller Azure Cosmos DB Java SDK, som vi ska lägga till som ett beroende i stället.

  11. I Project Explorer navigerar du till azure-cosmos-java-sample\src\com.microsoft.azure.cosmos.sample.dao\DocumentClientFactory.java och ersätter värdena HOST och MASTER_KEY med URI och PRIMÄRNYCKEL för ditt Azure Cosmos DB-konto och sparar sedan filen. Mer information finns i Steg 1. Skapa ett Azure Cosmos DB-databaskonto.

  12. Högerklicka på azure-cosmos-java-sample i Project Explorer, välj Skapa sökväg och välj sedan Konfigurera byggsökväg.

  13. På skärmen Java Build Path (Java Build Path ) i den högra rutan väljer du fliken Bibliotek och väljer sedan Lägg till externa JAR:er. Gå till platsen för filen lombok.jar och välj Öppna och välj sedan OK.

  14. Använd steg 12 för att öppna fönstret Egenskaper igen och välj sedan Riktade körningar i den vänstra rutan.

  15. På skärmen Riktade körningar väljer du Nytt, väljer Apache Tomcat v7.0 och sedan OK.

  16. Använd steg 12 för att öppna fönstret Egenskaper igen och välj sedan Projektfasader i den vänstra rutan.

  17. På skärmen Projektytor väljer du Dynamisk webbmodul och Java och sedan OK.

  18. Högerklicka på Tomcat v7.0 Server på localhost på fliken Servrar längst ned på skärmen och välj sedan Lägg till och ta bort.

  19. I fönstret Lägg till och ta bort flyttar du azure-cosmos-java-sample till rutan Konfigurerad och väljer sedan Slutför.

  20. Högerklicka på Tomcat v7.0 Server på localhost på fliken Servrar och välj sedan Starta om.

  21. Gå till http://localhost:8080/azure-cosmos-java-sample/ i en webbläsare och börja lägga till aktiviteter. Om du har ändrat portarnas standardvärden ändrar du 8080 till värdet du har valt.

  22. Information om hur du distribuerar projektet till en Azure-webbplats finns i Steg 6. Distribuera ditt program till Azure-webbplatser.

Nästa steg

Försöker du göra kapacitetsplanering för en migrering till Azure Cosmos DB? Du kan använda information om ditt befintliga databaskluster för kapacitetsplanering.