Oktatóanyag: Java-webalkalmazás létrehozása az Azure Cosmos DB és a NoSQL API használatával

A KÖVETKEZŐRE VONATKOZIK: NoSQL

Ez a Java-webalkalmazásokra vonatkozó oktatóanyag bemutatja, hogyan tárolhatja és érheti el az Azure App Service Web Appsben tárolt Java-alkalmazás adatait a Microsoft Azure Cosmos DB szolgáltatással. Hitelkártya vagy Azure-előfizetés nélkül beállíthat egy ingyenes Azure Cosmos DB-fiókot. Ebben a cikkben a következőkkel ismerkedhet meg:

  • Alapszintű JavaServer Pages- (JSP-) alkalmazás létrehozása az Eclipse-ben.
  • Az Azure Cosmos DB szolgáltatás használata az Azure Cosmos DB Java SDK-val.

Ez a Java-alkalmazásokra vonatkozó oktatóanyag bemutatja, hogyan hozhat létre egy webalapú feladatkezelő alkalmazást, amellyel feladatokat hozhat létre, kérhet le, valamint „kész” jelöléssel láthatja el azokat, ahogyan azt az alábbi illusztráció is mutatja. A rendszer a teendőlistában szereplő összes feladatot JSON-dokumentumként tárolja az Azure Cosmos DB-ben.

Saját teendőlista Java-alkalmazása

Tipp

Ez az alkalmazásfejlesztési oktatóanyag feltételezi, hogy rendelkezik korábbi tapasztalattal a Java használatát illetően. Ha még nem ismeri a Javát vagy az előfeltételt jelentő eszközöket, ajánlott letölteni a teljes teendők projektet a GitHubról, majd lefordítani azt a jelen cikk végén található utasítások segítségével. A projekt lefordítása után a cikk áttekintésével betekintést nyerhet a kódba a projekt környezetében.

A jelen Java-alkalmazásokra vonatkozó oktatóanyag előfeltételei

Az alkalmazásfejlesztési oktatóanyag elkezdéséhez az alábbiakkal kell rendelkeznie:

Ha első alkalommal telepíti ezeket az eszközöket, coreservlets.com bemutatja a telepítési folyamatot az Oktatóanyag: A TomCat7 telepítése és használata az Eclipse-tel című cikkük rövid útmutatójában.

Azure Cosmos DB-fiók létrehozása

Először hozzon létre egy Azure Cosmos DB-fiókot. Ha már rendelkezik fiókkal vagy az oktatóanyagban az Azure Cosmos DB Emulatort használja, továbbléphet a 2. lépés: Új Java JSP-alkalmazás létrehozása című lépésre.

  1. A Azure Portal menüben vagy a Kezdőlapon válassza az Erőforrás létrehozása lehetőséget.

  2. Keresse meg az Azure Cosmos DB-t. Válassza azAzure Cosmos DBlétrehozása> lehetőséget.

  3. Az Azure Cosmos DB-fiók létrehozása lapon válassza a Létrehozás lehetőséget az Azure Cosmos DB for NoSQL szakaszban.

    Az Azure Cosmos DB számos API-t biztosít:

    • NoSQL, dokumentumadatokhoz
    • PostgreSQL
    • MongoDB, dokumentumadatokhoz
    • Apache Cassandra
    • Tábla
    • Apache Gremlin, gráfadatokhoz

    A NoSQL API-val kapcsolatos további információkért lásd: Üdvözöljük az Azure Cosmos DB-ben.

  4. Az Azure Cosmos DB-fiók létrehozása lapon adja meg az új Azure Cosmos DB-fiók alapbeállításait.

    Beállítás Érték Leírás
    Előfizetés Előfizetés neve Válassza ki az Azure Cosmos DB-fiókhoz használni kívánt Azure-előfizetést.
    Erőforráscsoport Erőforráscsoport neve Válasszon ki egy erőforráscsoportot, vagy válassza az Új létrehozása lehetőséget, majd adjon meg egy egyedi nevet az új erőforráscsoportnak.
    Fiók neve Egyedi név Adjon meg egy nevet az Azure Cosmos DB-fiók azonosításához. Mivel documents.azure.com hozzá van fűzve az URI létrehozásához megadott névhez, használjon egyedi nevet. A név csak kisbetűket, számokat és kötőjelet (-) tartalmazhat. 3–44 karakter hosszúságúnak kell lennie.
    Hely A felhasználókhoz legközelebb eső régió Válassza ki az Azure Cosmos DB-fiókot üzemeltetéséhez használni kívánt földrajzi helyet. Használja a felhasználókhoz legközelebbi helyet, hogy a lehető leggyorsabb hozzáférést biztosítsa számukra az adatokhoz.
    Kapacitásmód Kiosztott átviteli sebesség vagy kiszolgáló nélküli A Kiosztott átviteli sebesség lehetőséget választva hozzon létre egy fiókot kiépített átviteli módban. A Kiszolgáló nélküli lehetőséget választva hozzon létre egy fiókot kiszolgáló nélküli módban.
    Ingyenes Azure Cosmos DB-kedvezmény alkalmazása Alkalmazás vagy Nem alkalmazható Az Ingyenes Azure Cosmos DB-szinttel az első 1000 RU/s- és 25 GB tárterületet kapja ingyenesen egy fiókban. További információ az ingyenes szintről.
    Fiók teljes átviteli sebességének korlátozása Kijelölve vagy nem Korlátozza az ezen a fiókon kiosztható átviteli sebesség teljes mennyiségét. Ez a korlát megakadályozza a kiosztott átviteli sebességhez kapcsolódó váratlan díjakat. Ezt a korlátot a fiók létrehozása után frissítheti vagy eltávolíthatja.

    Azure-előfizetésenként legfeljebb egy ingyenes szintű Azure Cosmos DB-fiókkal rendelkezhet, és a fiók létrehozásakor be kell jelentkeznie. Ha nem látja az ingyenes szint kedvezményének alkalmazását, az előfizetés egy másik fiókja már engedélyezve van az ingyenes szinttel.

    Képernyőkép az Azure Cosmos DB-fiók létrehozása oldalról.

    Megjegyzés

    A következő beállítások nem érhetők el, ha a Kiszolgáló nélküli lehetőséget választja Kapacitás módként:

    • Az ingyenes szint árengedményének alkalmazása
    • Fiók teljes átviteli sebességének korlátozása
  5. A Globális terjesztés lapon konfigurálja az alábbi részleteket. A rövid útmutató alapértelmezett értékeit a következő módon hagyhatja meg:

    Beállítás Érték Leírás
    Georedundancia Letiltás Engedélyezze vagy tiltsa le a globális terjesztést a fiókjában a régió párosításával egy párrégióval. Később további régiókat is felvehet a fiókjába.
    Többrégiós írók Letiltás A többrégiós írási képesség lehetővé teszi, hogy kihasználja a kiosztott átviteli sebességet az adatbázisok és tárolók számára világszerte.
    Rendelkezésre állási zónák Letiltás Availability Zones segít az alkalmazás rendelkezésre állásának és rugalmasságának további javításában.

    Megjegyzés

    A következő beállítások nem érhetők el, ha a Kiszolgáló nélküli lehetőséget választja kapacitás módként az előző Alapismeretek lapon:

    • Georedundancia
    • Többrégiós írók
  6. További részleteket az alábbi lapon is konfigurálhat:

    • Hálózatkezelés. Virtuális hálózatról való hozzáférés konfigurálása.
    • Biztonsági mentési szabályzat. Konfigurálja az időszakos vagy folyamatos biztonsági mentési szabályzatot.
    • Titkosítás. Használjon szolgáltatás által felügyelt kulcsot vagy ügyfél által felügyelt kulcsot.
    • Címkék. A címkék név-érték párok, amelyek lehetővé teszik az erőforrások kategorizálását és a konszolidált számlázás megtekintését úgy, hogy ugyanazt a címkét több erőforrásra és erőforráscsoportra alkalmazza.
  7. Válassza az Áttekintés + létrehozás lehetőséget.

  8. Tekintse át a fiókbeállításokat, majd válassza a Létrehozás lehetőséget. A fiók létrehozása néhány percet vesz igénybe. Várja meg, amíg a portállapon megjelenik az Üzembe helyezés befejeződött.

    Képernyőkép arról, hogy az üzembe helyezés befejeződött.

  9. Válassza az Erőforrás megnyitása lehetőséget az Azure Cosmos DB-fiók lapjának megtekintéséhez.

    Képernyőkép az Azure Cosmos DB-fiók oldalról.

Lépjen az Azure Cosmos DB-fiók lapjára, és válassza a Kulcsok lehetőséget. Másolja ki a következőként létrehozott webalkalmazásban használni kívánt értékeket.

Képernyőkép a Azure Portal az Azure Cosmos DB-fiók oldalán kiemelt Kulcsok gombbal

A Java JSP-alkalmazás létrehozása

JSP-alkalmazás létrehozása:

  1. Először egy Java-projekt létrehozásával kezdjük. Indítsa el az Eclipse-t, majd válassza a Fájl, az Új, majd a Dinamikus webprojekt lehetőséget. Ha nem látható, hogy a Dinamikus webprojekt elérhető projektként szerepel-e, tegye a következőket: Válassza a Fájl lehetőséget, válassza az Új lehetőséget, válassza a Projekt... lehetőséget, bontsa ki a Web elemet, válassza a Dinamikus webes projekt lehetőséget, és válassza a Tovább gombot.

    JSP Java-alkalmazások fejlesztése

  2. Adjon meg egy projektnevet a Projektnév mezőbe, majd a Cél futtatókörnyezet legördülő menüben válasszon ki egy értéket (például Apache Tomcat v7.0), majd válassza a Befejezés lehetőséget. A tervezett futásidő megadása lehetővé teszi, hogy helyileg, az Eclipse-ben is futtathassa projektjét.

  3. Az Eclipse Project Explorer (Projektböngésző) nézetében bontsa ki a projektet. Kattintson a jobb gombbal a WebContent elemre, válassza az Új, majd a JSP-fájl lehetőséget.

  4. A New JSP File (Új JSP-fájl) párbeszédablakban nevezze el a fájlt az alábbi módon: index.jsp. Tartsa a szülőmappát WebContentként az alábbi ábrán látható módon, majd válassza a Tovább gombot.

    Új JSP-fájl létrehozása – Java-alkalmazásokra vonatkozó oktatóanyag

  5. A JSP-sablon kiválasztása párbeszédpanelen az oktatóanyag céljára válassza az Új JSP-fájl (html) lehetőséget, majd válassza a Befejezés lehetőséget.

  6. Amikor megnyílik az index.jsp fájl az Eclipse-ben, adjon hozzá szöveget a meglévő<body> elemen belüli "Helló világ!" alkalmazás! megjelenítéséhez. A frissített <body> tartalomnak az alábbi kódhoz kell hasonlítania:

    <body>
      <% out.println("Hello World!"); %>
    </body>
    
  7. Mentse az index.jsp fájlt.

  8. Ha a 2. lépésben beállít egy cél futtatókörnyezetet, válassza a Project , majd a Futtatás lehetőséget a JSP-alkalmazás helyi futtatásához:

    Hello World – Java-alkalmazásokra vonatkozó oktatóanyag

Az SQL Java SDK telepítése

Az SQL Java SDK, valamint annak függőségei a legegyszerűbben az Apache Maven használatával kérhetők le. Ehhez az alábbi lépések végrehajtásával át kell alakítania a projektet Maven-projektté:

  1. Kattintson a jobb gombbal a projektre a Project Explorerben, válassza a Konfigurálás, majd a Konvertálás Maven-projektté lehetőséget.

  2. Az Új POM létrehozása ablakban fogadja el az alapértelmezett beállításokat, és válassza a Befejezés lehetőséget.

  3. A Project Explorer (Projektböngésző) nézetben nyissa meg a pom.xml fájlt.

  4. A Függőségek lap Függőségek paneljén válassza a Hozzáadás lehetőséget.

  5. A Select Dependency (Függőség kiválasztása) ablakban tegye a következőket:

    • A Csoportazonosító mezőbe írja be a következőt com.azure: .
    • Az Összetevő azonosítója mezőbe írja be a következőt azure-cosmos: .
    • A Verzió mezőbe írja be a következőt 4.11.0: .

    A csoportazonosítóhoz és az összetevő-azonosítóhoz tartozó függőségi XML-t közvetlenül is hozzáadhatja a pom.xml fájlhoz:

    <dependency>
      <groupId>com.azure</groupId>
      <artifactId>azure-cosmos</artifactId>
      <version>4.11.0</version>
    </dependency>
    
  6. Válassza az OK gombot , és a Maven telepíti az SQL Java SDK-t, vagy menti a pom.xml fájlt.

Az Azure Cosmos DB szolgáltatás használata a Java-alkalmazásban

Most adja hozzá a modelleket, a nézeteket és a vezérlőket a webalkalmazáshoz.

Modell hozzáadása

Először is definiáljunk egy modellt egy új TodoItem.java fájlban. Az TodoItem osztály meghatározza egy elem sémáját a getter és a setter metódusokkal együtt:

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

Az Adatelérési objektum (DAO) osztályainak hozzáadása

Hozzon létre egy adatelérési objektumot (DAO) a ToDo-elemek Azure Cosmos DB-ben való megőrzésének absztrakt megőrzéséhez. Ha menteni szeretné a teendőket egy gyűjteménybe, az ügyfélnek tudnia kell, melyik adatbázisban és gyűjteményben kívánja azt megőrizni (ahogy erre az önhivatkozások is hivatkoznak). Általában érdemes gyorsítótárazni az adatbázist és a gyűjteményt, amennyiben ez lehetséges, így elkerülhető az adatbázissal való fölösleges üzenetváltás.

  1. Az Azure Cosmos DB szolgáltatás meghívásához új cosmosClient objektumot kell létrehoznia. Általában a legjobb, ha újra felhasználja az objektumot ahelyett cosmosClient , hogy minden további kéréshez új ügyfelet hoz létre. Az ügyfelet újra felhasználhatja az osztályon belüli cosmosClientFactory definiálásával. Frissítse az 1. lépésben mentett HOST és MASTER_KEY értékeket. Cserélje le a HOST változót az URI-ra, és cserélje le a MASTER_KEY az ELSŐDLEGES KULCSra. A cosmosClientFactory.java fájlban az alábbi kóddal hozza létre az CosmosClientFactory osztályt:

    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. Hozzon létre egy új TodoDao.java fájlt, és adja hozzá az osztályt a TodoDao teendőelemek létrehozásához, frissítéséhez, olvasásához és törléséhez:

    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. Hozzon létre egy új MockDao.java fájlt, és adja hozzá az MockDao osztályt. Ez az osztály implementálja az osztályt a TodoDao CRUD-műveletek végrehajtásához az elemeken:

    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. Hozzon létre egy új DocDbDao.java fájlt, és adja hozzá az osztályt DocDbDao . Ez az osztály olyan kódot határoz meg, amely megőrzi a TodoItemeket a tárolóban, lekéri az adatbázist és a gyűjteményt, ha létezik, vagy ha nem létezik, hozzon létre egy újat. Ez a példa a Gson használatával szerializálja és deszerializálja a TodoItem Egyszerű régi Java-objektumokat (POJO-kat) JSON-dokumentumokba. Ha menteni szeretné a teendőket egy gyűjteménybe, az ügyfélnek tudnia kell, melyik adatbázisban és gyűjteményben kívánja azt megőrizni (ahogy erre az önhivatkozások is hivatkoznak). Ez az osztály a segédfüggvényt is definiálja a dokumentumok önhivatkozás helyett egy másik attribútum (pl. "AZONOSÍTÓ") szerinti lekéréséhez. A segédmetódussal azonosító alapján lekérhet egy TodoItem JSON-dokumentumot, majd deszerializálhatja azt egy POJO-ba.

    Az ügyfélobjektummal cosmosClient sql-lekérdezéssel is lekérheti a TodoItems gyűjteményét vagy listáját. Végül definiálja a törlési módszert a TodoItem listából való törléséhez. A következő kód az osztály tartalmát DocDbDao mutatja:

    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. Ezután hozzon létre egy új TodoDaoFactory.java fájlt, és adja hozzá az TodoDaoFactory új DocDbDao objektumot létrehozó osztályt:

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

Vezérlő hozzáadása

Adja hozzá a TodoItemController vezérlőt az alkalmazáshoz. Ebben a projektben a Project Lombok nevű projekt használatával hozzuk létre a konstruktort, a beolvasókat, a beállítókat és a felépítőt. Másik lehetőségként manuálisan is megírhatja ezt a kódot, vagy létrehozhatja az IDE-t:

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

servlet létrehozása

Ezután hozzon létre egy servletet, amely a HTTP-kéréseket a vezérlőnek irányítja. Hozza létre az ApiServlet.java fájlt, és definiálja alatta a következő kódot:

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

A Java-alkalmazás többi részének összekapcsolása

Most, hogy befejeztük a szórakoztató biteket, már csak egy gyors felhasználói felület létrehozása és a DAO-hoz való bekábelelése maradt.

  1. A felhasználó számára való megjelenítéshez webes felhasználói felületre van szüksége. Írjuk újra a korábban létrehozott index.jsp fájlt a következő kóddal:

    <%@ 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. Végül írjon néhány ügyféloldali JavaScriptet a webes felhasználói felület és a servlet összekapcsolásához:

    /**
     * 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. Most már csak le kell tesztelni az alkalmazást. Futtassa az alkalmazást helyileg, és adjon hozzá néhány teendőt. Ehhez adja meg az elemek nevét és kategóriáját, majd kattintson az Add Task (Feladat hozzáadása) elemre. Az elem megjelenése után frissítheti, hogy befejeződött-e a jelölőnégyzet bejelölésével, majd a Feladatok frissítése gombra kattintva.

Java-alkalmazás üzembe helyezése az Azure-webhelyeken

Az Azure Websites megkönnyíti a Java-alkalmazások telepítését. Nincs más dolga, mint exportálni az alkalmazást WAR-fájlként, majd feltölteni azt egy forráskezelő rendszer (pl. Git) vagy FTP segítségével.

  1. Ha WAR-fájlként szeretné exportálni az alkalmazást, kattintson a jobb gombbal a projektre a Project Explorerben, válassza az Exportálás, majd a WAR-fájl lehetőséget.

  2. A WAR Export (WAR-fájl exportálása) ablakban tegye a következőket:

    • A Webes projekt mezőbe írja be az azure-cosmos-java-sample kifejezést.
    • A Destination (Cél) mezőben válassza ki, hova szeretné menteni a WAR-fájlt.
    • Válassza a Befejezés gombot.
  3. Most, hogy megvan a WAR-fájl, egyszerűen töltse fel az Azure Websites webapps könyvtárába. A fájl feltöltésével kapcsolatos további információkért lásd a Java-alkalmazások az Azure App Service Web Appshoz való hozzáadását ismertető cikket. Miután feltöltötte a WAR-fájlt a webapps könyvtárba, a futtatókörnyezet észleli, hogy hozzáadta, és automatikusan betölti.

  4. A kész termék megtekintéséhez lépjen a http://YOUR\_SITE\_NAME.azurewebsites.net/azure-cosmos-java-sample/ webhelyre, és kezdje meg a feladatok hozzáadását.

A projekt beszerzése a GitHubról

A jelen oktatóanyag minden példáját megtalálhatja a GitHubról elérhető todo (teendők) projektben. A teendők projekt Eclipse-be történő importálásához győződjön meg arról, hogy rendelkezik az Előfeltételek szakaszban ismertetett szoftverekkel és erőforrásokkal, majd tegye a következőket:

  1. Telepítse a Project Lombok nevű projektet. A projekt konstruktorainak, beolvasóinak, beállítóinak létrehozása a Lombok használatával történik. A lombok.jar fájl letöltése után kattintson rá duplán annak telepítéséhez, vagy telepítse azt a parancssorból.

  2. Ha az Eclipse meg van nyitva, zárja be, és indítsa el újra a Lombok betöltéséhez.

  3. Az Eclipse-ben a Fájl menüben válassza az Importálás lehetőséget.

  4. Az Importálás ablakban válassza a Git, a Projektek a Gitből, majd a Tovább lehetőséget.

  5. Az Adattár forrásának kiválasztása képernyőn válassza az URI klónozása lehetőséget.

  6. A Forrás Git-adattár képernyő URI mezőjébe írja be https://github.com/Azure-Samples/azure-cosmos-java-sql-api-todo-appa kifejezést, majd válassza a Tovább gombot.

  7. Az Ág kiválasztása képernyőn győződjön meg arról, hogy a elem ki van jelölve, majd válassza a Tovább gombot.

  8. A Helyi cél képernyőn válassza a Tallózás lehetőséget egy olyan mappa kiválasztásához, amelybe az adattár másolható, majd válassza a Tovább gombot.

  9. A Projektek importálásához használandó varázsló kiválasztása képernyőn győződjön meg arról, hogy a Meglévő projektek importálása lehetőség van kiválasztva, majd válassza a Tovább gombot.

  10. A Projektek importálása képernyőn törölje a DocumentDB-projekt kijelölését, majd válassza a Befejezés lehetőséget. A DocumentDB-projekt tartalmazza az Azure Cosmos DB Java SDK-t, amelyet inkább függőségként adunk hozzá.

  11. A Project Explorerben lépjen az azure-cosmos-java-sample\src\com.microsoft.azure.cosmos.sample.dao\DocumentClientFactory.java helyre, és cserélje le a HOST és MASTER_KEY értékeket az Azure Cosmos DB-fiók URI-jára és ELSŐDLEGES KULCSára, majd mentse a fájlt. További információ: 1. lépés. Hozzon létre egy Azure Cosmos DB-adatbázisfiókot.

  12. A Project Explorerben kattintson a jobb gombbal az azure-cosmos-java-sample elemre, válassza az Elérési út létrehozása, majd az Összeállítási útvonal konfigurálása lehetőséget.

  13. A Java Build Path (Java buildelési útvonal ) képernyő jobb oldali ablaktábláján válassza a Könyvtárak lapot, majd válassza a Külső JAR-ek hozzáadása lehetőséget. Keresse meg a lombok.jar fájl helyét, és válassza a Megnyitás, majd az OK gombot.

  14. A 12. lépésben nyissa meg ismét a Tulajdonságok ablakot, majd a bal oldali panelen válassza a Célzott futtatókörnyezetek lehetőséget.

  15. A Célzott futtatókörnyezetek képernyőn válassza az Új lehetőséget, válassza az Apache Tomcat 7.0-s verziót, majd kattintson az OK gombra.

  16. A 12. lépésben nyissa meg ismét a Tulajdonságok ablakot, majd a bal oldali panelen válassza a Projekt aspektusai lehetőséget.

  17. A Projekt aspektusai képernyőn válassza a Dinamikus webmodul és a Java lehetőséget, majd kattintson az OK gombra.

  18. A képernyő alján található Kiszolgálók lapon kattintson a jobb gombbal a Tomcat v7.0 Server at localhost elemre, majd válassza a Hozzáadás és eltávolítás parancsot.

  19. A Hozzáadás és eltávolítás ablakban helyezze át az azure-cosmos-java-sample mezőt a Konfigurált mezőbe, majd válassza a Befejezés lehetőséget.

  20. A Kiszolgálók lapon kattintson a jobb gombbal a Tomcat v7.0 Server at localhost elemre, majd válassza az Újraindítás lehetőséget.

  21. Egy böngészőből lépjen a http://localhost:8080/azure-cosmos-java-sample/ címre, és kezdje el hozzáadni a feladatait a listához. Ügyeljen arra, hogy ha módosította a portok alapértelmezett értékét, akkor a 8080 értéket módosítsa a választott értékre.

  22. Ha a projektet egy Azure-webhelyen szeretné üzembe helyezni, tekintse meg a 6. lépést. Helyezze üzembe az alkalmazást az Azure-webhelyeken.

Következő lépések

Kapacitástervezést szeretne végezni az Azure Cosmos DB-be való migráláshoz? A kapacitástervezéshez használhatja a meglévő adatbázisfürtre vonatkozó információkat.