Tutorial: Erstellen einer Java-Webanwendung mithilfe von Azure Cosmos DB und der API für NoSQL
GILT FÜR: NoSQL
In diesem Tutorial zur Java-Webanwendung erfahren Sie, wie Sie den Microsoft Azure Cosmos DB-Dienst verwenden, um Daten einer in Azure App Service-Web-Apps gehosteten Java-Anwendung zu speichern und abzurufen. Sie können ein kostenloses Azure Cosmos DB-Testkonto einrichten, ohne eine Kreditkarte oder ein Azure-Abonnement zu benötigen. In diesem Artikel lernen Sie Folgendes:
- Erstellen einer einfachen JSP-Anwendung (JavaServer Pages) in Eclipse
- Verwenden des Azure Cosmos DB-Diensts mit dem Azure Cosmos DB-Java-SDK
In diesem Java-Anwendungslernprogramm erfahren Sie, wie Sie eine webbasierte Aufgabenverwaltungsanwendung erstellen, mit der Sie Aufgaben erstellen, abrufen und als abgeschlossen kennzeichnen können, wie in der folgenden Abbildung gezeigt. Die einzelnen Aufgaben in der Aufgabenliste sind als JSON-Dokumente in Azure Cosmos DB gespeichert.
Tipp
In diesem Lernprogramm zur Anwendungsentwicklung wird davon ausgegangen, dass Sie bereits Erfahrung mit Java haben. Wenn Sie noch nicht mit Java oder den erforderlichen Tools vertraut sind, wird empfohlen, das vollständige todo-Projekt von GitHub herunterzuladen und gemäß den Anweisungen am Ende dieses Artikels zu erstellen. Nachdem Sie das Projekt erstellt haben, können Sie den Artikel lesen, um Einblick in den Code im Kontext des Projekts zu erhalten.
Voraussetzungen für dieses Java-Webanwendungstutorial
Bevor Sie mit diesem Lernprogramm zur Anwendungsentwicklung beginnen, benötigen Sie Folgendes:
Falls Sie nicht über ein Abonnement verfügen, können Sie ein kostenloses Azure Cosmos DB-Testkonto einrichten, ohne eine Kreditkarte oder ein Azure-Abonnement zu benötigen.
Sie können Azure Cosmos DB kostenlos testen – ohne Azure-Abonnement und unverbindlich. Alternativ können Sie ein Azure Cosmos DB-Konto im Free-Tarif erstellen, bei dem die ersten 1000 RUs/Sek. sowie 25 GB Speicher kostenlos sind. Sie können auch den Azure Cosmos DB-Emulator mit dem URI
https://localhost:8081
verwenden. Informationen zur Verwendung des Schlüssels mit dem Emulator finden Sie unter Verwenden des Azure Cosmos-Emulators für lokale Entwicklungs- und Testvorgänge.Eine Azure-Website mit aktivierter Java-Laufzeitumgebung (z. B. Tomcat oder Jetty).
Falls Sie diese Tools zum ersten Mal installieren, finden Sie auf „coreservlets.com“ eine schrittweise Anleitung für den Installationsvorgang. Diese befindet sich im Abschnitt „Schnellstart“ im Lernprogramm: TomCat7 installieren und mit Eclipse verwenden.
Erstellen eines Azure Cosmos DB-Kontos
Wir beginnen, indem wir ein Azure Cosmos DB-Konto erstellen. Falls Sie bereits ein Konto besitzen oder den Azure Cosmos DB-Emulator für dieses Tutorial verwenden, können Sie mit Schritt 2: Erstellen der Java JSP-Anwendung fortfahren.
Wählen Sie im Menü des Azure-Portals oder auf der Startseite die Option Ressource erstellen aus.
Suche Sie nach Azure Cosmos DB. Wählen Sie Erstellen>Azure Cosmos DB aus.
Wählen Sie auf der Seite Erstellen eines Azure Cosmos DB-Kontos im Abschnitt Azure Cosmos DB for NoSQL die Option Erstellen aus.
Azure Cosmos DB bietet mehrere APIs:
- NoSQL, für Dokumentdaten
- PostgreSQL
- MongoDB, für Dokumentdaten
- Apache Cassandra
- Tabelle
- Apache Gremlin, für Graphdaten
Weitere Informationen zur API für NoSQL finden Sie unter Willkommen bei Azure Cosmos DB.
Geben Sie auf der Seite Azure Cosmos DB-Konto erstellen die grundlegenden Einstellungen für das neue Azure Cosmos DB-Konto ein.
Einstellung Wert BESCHREIBUNG Subscription Abonnementname Wählen Sie das Azure-Abonnement aus, das Sie für dieses Azure Cosmos DB-Konto verwenden möchten. Ressourcengruppe Ressourcengruppenname Wählen Sie eine Ressourcengruppe aus, oder wählen Sie Neu erstellen aus, und geben Sie einen eindeutigen Namen für die Ressourcengruppe ein. Kontoname Ein eindeutiger Name Geben Sie einen Namen zur Identifizierung Ihres Azure Cosmos DB-Kontos ein. Da documents.azure.com an den Namen angefügt wird, die Sie für die URI-Erstellung angeben, muss der Name eindeutig sein. Der Name darf nur Kleinbuchstaben, Zahlen und den Bindestrich (-) enthalten. Er muss 3–44 Zeichen umfassen. Standort Die Region, die Ihren Benutzern am nächsten liegt Wählen Sie einen geografischen Standort aus, an dem Ihr Azure Cosmos DB-Konto gehostet werden soll. Verwenden Sie den Standort, der Ihren Benutzern am nächsten ist, damit sie möglichst schnell auf die Daten zugreifen können. Kapazitätsmodus Bereitgestellter Durchsatz oder Serverlos Wählen Sie Bereitgestellter Durchsatz aus, um ein Konto im Modus Bereitgestellter Durchsatz zu erstellen. Wählen Sie Serverlos aus, um ein Konto im Modus Serverlos zu erstellen. Anwenden des Rabatts für den Free-Tarif von Azure Cosmos DB Anwenden oder Nicht anwenden Mit dem Azure Cosmos DB-Tarif „Free“ erhalten Sie die ersten 1.000 RUs/Sek. sowie 25 GB Speicher kostenlos in einem Konto. Weitere Informationen zum Tarif „Free“ Beschränken des gesamten Kontodurchsatzes Ausgewählt sein oder nicht Begrenzen Sie den Gesamtdurchsatz, der für dieses Konto bereitgestellt werden kann. Dieser Grenzwert verhindert unerwartete Gebühren im Zusammenhang mit bereitgestelltem Durchsatz. Sie können diesen Grenzwert anpassen oder entfernen, nachdem Ihr Konto erstellt wurde. Sie können pro Azure-Abonnement maximal ein Azure Cosmos DB-Konto im Free-Tarif einrichten und müssen sich beim Erstellen des Kontos registrieren. Wird die Option zum Anwenden des tarifspezifischen Rabatts für den Free-Tarif nicht angezeigt, bedeutet dies, dass bereits ein anderes Konto im Abonnement mit dem Free-Tarif aktiviert wurde.
Hinweis
Die folgenden Optionen sind nicht verfügbar, wenn Sie als Kapazitätsmodus die Option Serverlos auswählen:
- Tarifspezifischen Rabatt für den Free-Tarif anwenden
- Beschränken des gesamten Kontodurchsatzes
Konfigurieren Sie auf der Registerkarte Globale Verteilung die folgenden Details. Für diesen Schnellstart können Sie die Standardwerte beibehalten:
Einstellung Wert Beschreibung Georedundanz Deaktivieren Aktivieren oder deaktivieren Sie die globale Verteilung für Ihr Konto, indem Sie Ihre Region mit einer Region koppeln. Sie können später weitere Regionen zu Ihrem Konto hinzufügen. Schreibvorgänge in mehreren Regionen Deaktivieren Mit der Funktion zum Schreiben in mehreren Regionen können Sie den bereitgestellten Durchsatz für Ihre Datenbanken und Container in der ganzen Welt nutzen. Verfügbarkeitszonen Deaktivieren Verfügbarkeitszonen helfen Ihnen, die Verfügbarkeit und Resilienz Ihrer Anwendung weiter zu verbessern. Hinweis
Die folgenden Optionen sind nicht verfügbar, wenn Sie als Kapazitätsmodus auf der vorigen Seite Grundlagen die Option Serverlos auswählen:
- Georedundanz
- Schreibvorgänge in mehreren Regionen
Optional können Sie auf den folgenden Registerkarten weitere Details konfigurieren:
- Netzwerke. Konfigurieren des Zugriffs über virtuelle Netzwerke.
- Sicherungsrichtlinie. Konfigurieren Sie eine Richtlinie für regelmäßige oder fortlaufende Sicherungen.
- Verschlüsselung. Verwenden Sie entweder einen vom Dienst verwalteten Schlüssel oder einen kundenseitig verwalteten Schlüssel.
- Tags. Tags sind Name/Wert-Paare, die Ihnen das Kategorisieren von Ressourcen und die Anzeige einer konsolidierten Abrechnung ermöglichen, indem Sie dasselbe Tag auf mehrere Ressourcen und Ressourcengruppen anwenden.
Klicken Sie auf Überprüfen + erstellen.
Überprüfen Sie die Kontoeinstellungen, und wählen Sie anschließend Erstellen aus. Die Erstellung des Kontos dauert einige Minuten. Warten Sie, bis auf der Portalseite Ihre Bereitstellung wurde abgeschlossen. angezeigt wird.
Wählen Sie Zu Ressource wechseln aus, um zur Seite des Azure Cosmos DB-Kontos zu wechseln.
Navigieren Sie zur Seite des Azure Cosmos DB-Kontos, und wählen Sie Schlüssel aus. Kopieren Sie die Werte, die in der Webanwendung verwendet werden sollen, die Sie als Nächstes erstellen.
Erstellen der Java JSP-Anwendung
So erstellen Sie eine JSP-Anwendung
Zunächst beginnen wir mit der Erstellung eines Java-Projekts. Starten Sie Eclipse, und wählen Sie dann Datei aus, wählen Sie Neu aus, und wählen Sie dann Dynamic Web Project aus. Wenn Dynamic Web Project nicht als verfügbares Projekt aufgeführt wird, gehen Sie wie folgt vor: Wählen Sie Datei aus, wählen Sie Neu und dann Projekt aus. Erweitern Sie die Option Web, wählen Sie Dynamic Web Project aus, und wählen Sie anschließend Weiter aus.
Geben Sie einen Projektnamen in das Feld Projektname ein, wählen Sie optional im Dropdownmenü Ziellaufzeit einen Wert aus (z. B. Apache Tomcat v7.0), und wählen Sie dann Fertig stellen aus. Durch das Auswählen einer Ziellaufzeit können Sie das Projekt lokal über Eclipse ausführen.
Erweitern Sie in der Projektexplorer-Ansicht von Eclipse Ihr Projekt. Klicken Sie mit der rechten Maustaste auf WebContent, und wählen Sie dann New (Neu) und JSP File (JSP-Datei) aus.
Geben Sie der Datei im Dialogfeld Neue JSP-Datei den Namen index.jsp. Behalten Sie für den übergeordneten Ordner WebContent bei, wie in der folgenden Abbildung gezeigt, und wählen dann Weiter aus.
Wählen Sie im Dialogfeld JSP-Vorlage auswählen im Rahmen dieses Lernprogramms Neue JSP-Datei (HTML), und wählen Sie dann Fertig stellen aus.
Wenn in Eclipse die Datei index.jsp geöffnet wird, geben Sie in das vorhandene
<body>
-Element den anzuzeigenden Text Hello World! ein. Der aktualisierte Inhalt von<body>
sollte wie im folgenden Code aussehen:<body> <% out.println("Hello World!"); %> </body>
Speichern Sie die Datei index.jsp.
Wenn Sie in Schritt 2 eine Ziellaufzeitversion festgelegt haben, können Sie Projekt und dann Ausführen auswählen, um die JSP-Anwendung lokal auszuführen:
Installieren des SQL Java SDK
Das SQL Java SDK und seine Abhängigkeiten können am einfachsten über Apache Maveneinbezogen werden. Dazu müssen Sie mithilfe der folgenden Schritten das Projekt in ein Maven-Projekt konvertieren:
Klicken Sie mit der rechten Maustaste im Projektexplorer auf Ihr Projekt, wählen Sie dann Konfigurieren und anschließend die Option zur Umwandlung in ein Maven-Projekt aus.
Übernehmen Sie im Fenster Neue POM erstellen die Standardeinstellungen, und wählen Sie dann Fertig stellen aus.
Öffnen Sie die Datei „pom.xml“ im Projektexplorer.
Wählen Sie auf der Registerkarte Abhängigkeiten im Bereich Abhängigkeiten die Option Hinzufügen aus.
Gehen Sie im Fenster Abhängigkeit auswählen wie folgt vor:
- Geben Sie
com.azure
im Feld Gruppen-ID ein. - Geben Sie
azure-cosmos
im Feld Artefakt-ID ein. - Geben Sie
4.11.0
im Feld Version ein.
Alternativ können Sie den Abhängigkeits-XML-Code für die Gruppen-ID und die Artefakt-ID direkt in der Datei pom.xml hinzufügen:
<dependency> <groupId>com.azure</groupId> <artifactId>azure-cosmos</artifactId> <version>4.11.0</version> </dependency>
- Geben Sie
Wählen Sie OK aus, damit Maven das SQL Java SDK installiert, oder speichern Sie die Datei „pom.xml“.
Verwenden des Azure Cosmos DB-Diensts in Ihrer Java-Anwendung
Fügen Sie Ihrer Webanwendung als Nächstes die Modelle, Ansichten und Controller hinzu.
Hinzufügen eines Modells
Zunächst definieren Sie ein Modell in einer neuen Datei namens TodoItem.java. Die TodoItem
-Klasse definiert das Schema eines Elements zusammen mit der getter- und setter-Methode:
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;
}
}
Hinzufügen der DAO-Klassen (Data Access Object)
Erstellen Sie ein Datenzugriffsobjekt (Data Access Object, DAO), um das Speichern der Aufgabenelemente in Azure Cosmos DB zu abstrahieren. Um die Aufgabenelemente in einer Sammlung zu speichern, muss der Client wissen, in welcher Datenbank und Sammlung die Speicherung erfolgen soll (auf die durch eigene Links verwiesen wird). Im Allgemeinen empfiehlt es sich, die Datenbank und Sammlung nach Möglichkeit zwischenzuspeichern, um zusätzliche Roundtrips zur Datenbank zu vermeiden.
Um den Azure Cosmos DB-Dienst aufzurufen, müssen Sie ein neues
cosmosClient
-Objekt instanziieren. Im Allgemeinen empfiehlt es sich, dascosmosClient
-Objekt wiederzuverwenden, anstatt für jede nachfolgende Anforderung einen neuen Client zu erstellen. Sie können den Client wiederverwenden, indem Sie ihn in dercosmosClientFactory
-Klasse definieren. Aktualisieren Sie die Werte für HOST und MASTER_KEY, die Sie in Schritt 1 gespeichert haben. Ersetzen Sie die Variable HOST durch Ihren URI und MASTER_KEY durch ihren Primärschlüssel (PRIMARY KEY). Verwenden Sie den folgenden Code, um dieCosmosClientFactory
-Klasse in der Datei CosmosClientFactory.java zu erstellen: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; } }
Erstellen Sie eine neue Datei namens TodoDao.java, und fügen Sie die
TodoDao
-Klasse hinzu, um die Aufgabenelemente zu erstellen, zu aktualisieren, zu lesen und zu löschen: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); }
Erstellen Sie eine neue Datei namens MockDao.java, und fügen Sie die
MockDao
-Klasse hinzu. Diese Klasse implementiert dieTodoDao
-Klasse, um CRUD-Vorgänge für die Elemente auszuführen: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(); } }
Erstellen Sie eine neue Datei namens DocDbDao.java, und fügen Sie die
DocDbDao
-Klasse hinzu. Diese Klasse definiert den Code zum Speichern der Aufgabenelemente im Container und zum Abrufen von Datenbank und Sammlung (sofern vorhanden). Falls diese nicht vorhanden ist, wird sie neu erstellt. In diesem Beispiel wird Gson verwendet, um die TodoItem-POJOs (Plain Old Java Objects) in JSON-Dokumenten zu serialisieren bzw. zu deserialisieren. Um die Aufgabenelemente in einer Sammlung zu speichern, muss der Client wissen, in welcher Datenbank und Sammlung die Speicherung erfolgen soll (auf die durch eigene Links verwiesen wird). Diese Klasse definiert auch die Hilfsfunktion zum Abrufen der Dokumente durch ein anderes Attribut (z. B. „ID“) und nicht mit einem seiteninternen Link. Mit der Hilfsmethode können Sie ein TodoItem-JSON-Dokument nach „ID“ abrufen und dann zu einem POJO deserialisieren.Sie können auch das
cosmosClient
-Clientobjekt verwenden, um eine Sammlung oder Liste mit Aufgabenelementen mit einer SQL-Abfrage abzurufen. Abschließend definieren Sie die delete-Methode, um ein Aufgabenelement aus der Liste zu löschen. Mit dem folgenden Code zeigen Sie den Inhalt derDocDbDao
-Klasse an: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; } } }
Erstellen Sie als Nächstes eine neue Datei namens ToDoDaoFactory.java, und fügen Sie die
TodoDaoFactory
-Klasse hinzu, mit der ein neues DocDbDao-Objekt erstellt wird:package com.microsoft.azure.cosmos.sample.dao; public class TodoDaoFactory { private static TodoDao myTodoDao = new DocDbDao(); public static TodoDao getDao() { return myTodoDao; } }
Hinzufügen eines Controllers
Fügen Sie den Controller ToDoItemController zu Ihrer Anwendung hinzu. In diesem Projekt verwenden Sie Project Lombok, um den Konstruktor, die Getter, die Setter und einen Generator zu generieren. Alternativ können Sie diesen Code manuell eingeben oder von der IDE generieren lassen:
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);
}
}
Erstellen eines Servlets
Als Nächstes erstellen Sie ein Servlet zum Weiterleiten von HTTP-Anforderungen an den Controller. Erstellen Sie die Datei ApiServlet.java, und definieren Sie darin den folgenden Code:
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);
}
}
Einbinden der übrigen Java-App
Nachdem nun der angenehme Teil erledigt ist, müssen Sie nur noch eine bequeme Benutzeroberfläche erstellen und mit Ihrem DAO verbinden.
Sie benötigen eine Webbenutzeroberfläche, die dem Benutzer angezeigt wird. Dazu wird die zuvor erstellte Datei index.jsp mit dem folgenden Code neu geschrieben:
<%@ 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>
Schreiben Sie abschließend ein clientseitiges JavaScript, um die Webbenutzeroberfläche mit dem Servlet zu verbinden:
/** * 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(); });
Jetzt muss die Anwendung nur noch getestet werden. Führen Sie die Anwendung lokal aus, und fügen Sie einige Aufgabenelemente hinzu, indem Sie den Elementnamen und die Kategorie eingeben und auf Add Taskklicken. Wenn das Element angezeigt wird, können Sie den Abschlussstatus aktualisieren, indem Sie das Kontrollkästchen aktivieren und auf Aufgaben aktualisieren klicken.
Bereitstellen Ihrer Java-Anwendung auf Azure Websites
Azure-Websites gestaltet die Bereitstellung von Java-Anwendungen so einfach wie das Exportieren Ihrer Anwendung als WAR-Datei und das anschließende Hochladen über die Quellcodeverwaltung (z.B. Git) oder über FTP.
Zum Exportieren Ihrer Anwendung als WAR-Datei klicken Sie im Projektexplorer mit der rechten Maustaste auf das Projekt, und wählen Sie anschließend Exportieren und dann WAR-Datei aus.
Gehen Sie im Fenster WAR-Export wie folgt vor:
- Geben Sie in das Webprojektfeld „azure-cosmos-java-sample“ ein.
- Wählen Sie im Feld „Ziel“ ein Ziel aus, an dem die WAR-Datei gespeichert wird.
- Wählen Sie Fertig stellen aus.
Nachdem Sie jetzt über eine WAR-Datei verfügen, können Sie diese ganz einfach in das Verzeichnis webapps Ihrer Azure-Website hochladen. Anweisungen zum Hochladen der Datei finden Sie unter Hinzufügen einer Java-Anwendung zu Azure App Service-Web-Apps. Nachdem die WAR-Datei in das Verzeichnis „webapps“ hochgeladen wurde, erkennt die Laufzeitumgebung die hinzugefügte Datei und lädt sie automatisch.
Navigieren Sie zum Anzeigen Ihres fertigen Produkts zu
http://YOUR\_SITE\_NAME.azurewebsites.net/azure-cosmos-java-sample/
, und fügen Sie Ihre Aufgaben hinzu.
Abrufen des Projekts von GitHub
Alle Beispiele in diesem Tutorial befinden sich im Projekt todo auf GitHub. Zum Importieren des Todo-Projekts in Eclipse müssen Sie sicherstellen, dass Sie über die im Abschnitt Voraussetzungen aufgeführten Softwareanwendungen und Ressourcen verfügen. Gehen Sie anschließend folgendermaßen vor:
Installieren Sie das Projekt Lombok. Lombok wird verwendet, um Konstruktoren, Getter und Setter im Projekt zu generieren. Nachdem Sie die Datei „lombok.jar“ heruntergeladen haben, doppelklicken Sie auf die Datei, um sie zu installieren, installieren Sie sie über die Befehlszeile.
Wenn Eclipse geöffnet ist, schließen Sie es, und starten Sie es neu, um Lombok zu laden.
Wählen Sie in Eclipse im Menü Datei die Option Importieren aus.
Wählen Sie im Fenster Importieren die Option Git aus, wählen Sie Projekte von Git aus, und wählen Sie dann Weiter aus.
Wählen Sie auf dem Bildschirm Repositoryquelle auswählen die Option URI klonen aus.
Geben Sie im Bildschirm Source Git Repository (Quell-Git-Repository) im Feld URIhttps://github.com/Azure-Samples/azure-cosmos-java-sql-api-todo-app ein, und wählen Sie dann Next (Weiter) aus.
Stellen Sie auf dem Bildschirm Verzweigungsauswahl sicher, dass main ausgewählt ist, und wählen Sie dann Weiter aus.
Wählen Sie auf dem Bildschirm Lokales Ziel die Option Durchsuchen aus, um einen Ordner auszuwählen, in den das Repository kopiert werden kann, und wählen Sie dann Weiter aus.
Stellen Sie auf dem Bildschirm Assistent zum Importieren von Projekten auswählen sicher, dass Vorhandene Projekte importieren ausgewählt ist, und wählen Sie dann Weiter aus.
Heben Sie auf dem Bildschirm Projekte importieren die Auswahl des Projekts DocumentDB auf, und wählen Sie dann Fertig stellen aus. Das DocumentDB-Projekt enthält das Azure Cosmos DB-Java-SDK, das wir stattdessen als Abhängigkeit hinzufügen.
Navigieren Sie im Projektexplorer zu „azure-cosmos-java-sample\src\com.microsoft.azure.cosmos.sample.dao\DocumentClientFactory.java“, und ersetzen Sie die Werte „HOST“ und „MASTER_KEY“ durch den URI und den PRIMÄRSCHLÜSSEL für Ihr Azure Cosmos DB-Konto. Speichern Sie dann die Datei. Weitere Informationen finden Sie unter Schritt 1: Erstellen eines Azure Cosmos DB-Datenbankkontos.
Klicken Sie im Projektexplorer mit der rechten Maustaste auf azure-cosmos-java-sample, und wählen Sie Buildpfad und dann Buildpfad konfigurieren aus.
Wählen Sie auf dem Bildschirm Java-Buildpfad im rechten Bereich die Registerkarte Bibliotheken aus, und wählen Sie dann Externe JARs hinzufügen aus. Navigieren Sie zum Speicherort der Datei „lombok.jar“, und wählen Sie Öffnen und dann OK aus.
Verwenden Sie Schritt 12, um das Fenster Eigenschaften erneut zu öffnen, und wählen Sie dann im linken Bereich Vorgesehene Laufzeiten aus.
Wählen Sie auf dem Bildschirm Vorgesehene Laufzeiten die Option Neu aus, wählen Sie Apache Tomcat v7.0 aus, und wählen Sie dann OK aus.
Verwenden Sie Schritt 12, um das Fenster Eigenschaften erneut zu öffnen, und wählen Sie dann im linken Bereich Projektfacets aus.
Wählen Sie auf dem Bildschirm Projektfacets die Optionen Dynamisches Webmodul und Java aus, und wählen Sie dann OK aus.
Klicken Sie auf der Registerkarte Server am unteren Bildschirmrand mit der rechten Maustaste auf Tomcat v7.0-Server auf Localhost, und wählen Sie dann Hinzufügen und Entfernen aus.
Verschieben Sie azure-cosmos-java-sample im Fenster Hinzufügen und Entfernen in das Feld Konfiguriert, und wählen Sie dann Fertig stellen aus.
Klicken Sie auf der Registerkarte Server mit der rechten Maustaste auf den Server mit Tomcat v7.0 auf Localhost, und wählen Sie dann Neu starten aus.
Wechseln Sie in einem Browser zu
http://localhost:8080/azure-cosmos-java-sample/
, und beginnen Sie mit den Hinzufügungen zu Ihrer Aufgabenliste. Beachten Sie, dass Sie, wenn Sie die Standardwerte für den Port geändert haben, 8080 auf den Wert von Ihnen ausgewählten Wert ändern müssen.Informationen zum Bereitstellen Ihres Projekts auf einer Azure-Website finden Sie unter Schritt 6: Bereitstellen der Anwendung auf Azure-Websites.
Nächste Schritte
Versuchen Sie, die Kapazitätsplanung für eine Migration zu Azure Cosmos DB durchzuführen? Sie können Informationen zu Ihrem vorhandenen Datenbankcluster für die Kapazitätsplanung verwenden.
- Wenn Sie lediglich die Anzahl der virtuellen Kerne und Server in Ihrem vorhandenen Datenbankcluster kennen, lesen Sie die Informationen zum Schätzen von Anforderungseinheiten mithilfe von virtuellen Kernen oder virtuellen CPUs.
- Wenn Sie die typischen Anforderungsraten für Ihre aktuelle Datenbankworkload kennen, lesen Sie die Informationen zum Schätzen von Anforderungseinheiten mit dem Azure Cosmos DB-Kapazitätsplaner