Samouczek: tworzenie aplikacji internetowej Java przy użyciu usługi Azure Cosmos DB i interfejsu API dla noSQL
DOTYCZY: NoSQL
W tym samouczku aplikacji internetowej w języku Java pokazano, jak przy użyciu usługi Microsoft Azure Cosmos DB przechowywać dane i uzyskiwać do nich dostęp z poziomu aplikacji w języku Java hostowanej w usłudze Azure App Service Web Apps. Bez karty kredytowej lub subskrypcji platformy Azure możesz skonfigurować bezpłatne konto Wypróbuj usługę Azure Cosmos DB. Z tego artykułu dowiesz się:
- Jak utworzyć prostą aplikację JSP (JavaServer Pages) w środowisku Eclipse.
- Jak pracować z usługą Azure Cosmos DB przy użyciu zestawu SDK języka Java dla usługi Azure Cosmos DB.
W tym samouczku aplikacji w języku Java pokazano, jak utworzyć aplikację do zarządzania zadaniami opartą na sieci Web, która pozwala na tworzenie, pobieranie i oznaczanie zadań jako ukończonych, jak pokazano na poniższej ilustracji. Każde z zadań z listy zadań do wykonania będzie przechowywane jako dokument JSON w usłudze Azure Cosmos DB.
Napiwek
Ten samouczek tworzenia aplikacji zakłada, że masz już pewne doświadczenie w korzystaniu z języka Java. Jeśli nie znasz języka Java lub wstępnie wymaganych narzędzi, zalecamy pobranie kompletnego projektu todo z usługi GitHub i skompilowanie go zgodnie z instrukcjami znajdującymi się na końcu artykułu. Po jego skompilowaniu możesz przejrzeć ten artykuł, aby przeanalizować kod w kontekście projektu.
Wymagania wstępne dotyczące tego samouczka aplikacji internetowej w języku Java
Przed rozpoczęciem korzystania z tego samouczka tworzenia aplikacji należy dysponować następującymi elementami:
Jeśli nie masz subskrypcji platformy Azure bez karty kredytowej lub subskrypcji platformy Azure, możesz skonfigurować bezpłatne konto Wypróbuj usługę Azure Cosmos DB.
Możesz bezpłatnie wypróbować usługę Azure Cosmos DB bez subskrypcji platformy Azure i bez zobowiązania. Alternatywnie możesz utworzyć konto warstwy bezpłatnej usługi Azure Cosmos DB z pierwszymi 1000 RU/s i 25 GB miejsca do magazynowania bezpłatnie. Możesz również użyć emulatora usługi Azure Cosmos DB z identyfikatorem URI .
https://localhost:8081
Aby uzyskać klucz do użycia z emulatorem, zobacz Uwierzytelnianie żądań.
Jeśli instalujesz te narzędzia po raz pierwszy, coreservlets.com zawiera przewodnik po procesie instalacji w sekcji Szybki start w artykule Samouczek: instalowanie serwera TomCat7 i używanie go z programem Eclipse .
Tworzenie konta usługi Azure Cosmos DB
Zacznijmy od utworzenia konta usługi Azure Cosmos DB. Jeśli masz już konto lub jeśli korzystasz z emulatora usługi Azure Cosmos DB na potrzeby tego samouczka, możesz od razu przejść do sekcji Krok 2. Tworzenie aplikacji Java JSP.
W menu witryny Azure Portal lub na stronie głównej wybierz pozycję Utwórz zasób.
Wyszukaj usługę Azure Cosmos DB. Wybierz pozycję Utwórz usługę>Azure Cosmos DB.
Na stronie Tworzenie konta usługi Azure Cosmos DB wybierz opcję Utwórz w sekcji Azure Cosmos DB for NoSQL.
Usługa Azure Cosmos DB udostępnia kilka interfejsów API:
- NoSQL — dane dokumentu
- PostgreSQL
- MongoDB dla danych dokumentu
- Apache Cassandra
- Table
- Apache Gremlin — dla danych grafu
Aby dowiedzieć się więcej na temat interfejsu API dla NoSQL, zobacz Witamy w usłudze Azure Cosmos DB.
Na stronie Tworzenie konta usługi Azure Cosmos DB wprowadź podstawowe ustawienia nowego konta usługi Azure Cosmos DB.
Ustawienie Wartość Opis Subskrypcja Nazwa subskrypcji Wybierz subskrypcję platformy Azure, która ma być używana dla tego konta usługi Azure Cosmos DB. Grupa zasobów Nazwa grupy zasobów Wybierz grupę zasobów lub wybierz pozycję Utwórz nową, a następnie wprowadź unikatową nazwę nowej grupy zasobów. Nazwa konta Unikatowa nazwa Wprowadź nazwę, aby zidentyfikować konto usługi Azure Cosmos DB. Ponieważ adres documents.azure.com jest dołączany do podanej nazwy w celu utworzenia identyfikatora URI, użyj unikatowej nazwy. Nazwa może zawierać tylko małe litery, cyfry i znak łącznika (-). Musi mieć od 3 do 44 znaków. Lokalizacja Region najbliżej Twoich użytkowników Wybierz lokalizację geograficzną, w której będzie hostowane konto usługi Azure Cosmos DB. Użyj lokalizacji znajdującej się najbliżej Twoich użytkowników, aby zapewnić im najszybszy dostęp do danych. Tryb wydajności Aprowizowana przepływność lub bezserwerowa Wybierz pozycję Aprowizowana przepływność , aby utworzyć konto w trybie aprowizowanej przepływności . Wybierz pozycję Bezserwerowe , aby utworzyć konto w trybie bezserwerowym . Stosowanie rabatu za bezpłatną warstwę usługi Azure Cosmos DB Zastosuj lub nie zastosuj W warstwie Bezpłatna usługi Azure Cosmos DB uzyskasz pierwsze 1000 RU/s i 25 GB miejsca do magazynowania bezpłatnie na koncie. Dowiedz się więcej o warstwie Bezpłatna. Ograniczanie całkowitej przepływności konta Wybrane lub nie Ogranicz łączną ilość przepływności, którą można aprowizować na tym koncie. Ten limit uniemożliwia nieoczekiwane opłaty związane z aprowizowaną przepływnością. Możesz zaktualizować lub usunąć ten limit po utworzeniu konta. Możesz mieć maksymalnie jedno konto usługi Azure Cosmos DB w warstwie Bezpłatna na subskrypcję platformy Azure i musi wyrazić zgodę podczas tworzenia konta. Jeśli nie widzisz opcji zastosowania rabatu na warstwę Bezpłatna, inne konto w subskrypcji zostało już włączone z warstwą Bezpłatna.
Uwaga
Następujące opcje nie są dostępne w przypadku wybrania opcji Bezserwerowa jako tryb pojemności:
- Zastosuj rabat w warstwie Bezpłatna
- Ograniczanie całkowitej przepływności konta
Na karcie Dystrybucja globalna skonfiguruj następujące szczegóły. Możesz pozostawić wartości domyślne dla tego przewodnika Szybki start:
Ustawienie Wartość Opis Nadmiarowość geograficzna Wyłącz Włącz lub wyłącz dystrybucję globalną na koncie, łącząc region z regionem pary. Później możesz dodać więcej regionów do swojego konta. Zapis w wielu regionach Wyłącz Funkcja zapisu w wielu regionach umożliwia korzystanie z aprowizowanej przepływności dla baz danych i kontenerów na całym świecie. Strefy dostępności Wyłącz Strefy dostępności pomóc w dalszym ulepszaniu dostępności i odporności aplikacji. Uwaga
Następujące opcje nie są dostępne, jeśli wybierzesz pozycję Bezserwerowa jako tryb pojemności na poprzedniej stronie Podstawowe :
- Nadmiarowość geograficzna
- Zapis w wielu regionach
Opcjonalnie możesz skonfigurować więcej szczegółów na następujących kartach:
- Sieć. Konfigurowanie dostępu z sieci wirtualnej.
- Zasady tworzenia kopii zapasowych. Skonfiguruj zasady okresowych lub ciągłych kopii zapasowych.
- Szyfrowanie. Użyj klucza zarządzanego przez usługę lub klucza zarządzanego przez klienta.
- Tagi. Tagi to pary nazw i wartości umożliwiające kategoryzowanie zasobów oraz wyświetlanie skonsolidowanych informacji na temat rozliczeń przez zastosowanie tego samego tagu względem wielu zasobów i grup zasobów.
Wybierz pozycję Przejrzyj i utwórz.
Przejrzyj ustawienia konta, a następnie wybierz pozycję Utwórz. Utworzenie konta trwa kilka minut. Poczekaj na wyświetlenie komunikatu Wdrożenie zostało ukończone na stronie portalu.
Wybierz pozycję Przejdź do zasobu, aby przejść do strony konta usługi Azure Cosmos DB.
Przejdź do strony konta usługi Azure Cosmos DB i wybierz pozycję Klucze. Skopiuj wartości do użycia w utworzonej aplikacji internetowej.
Tworzenie aplikacji Java JSP
Aby utworzyć aplikację JSP:
Najpierw zaczniemy od utworzenia projektu Java. Uruchom środowisko Eclipse, a następnie wybierz pozycję Plik, wybierz pozycję Nowy, a następnie wybierz pozycję Dynamiczny projekt sieci Web. Jeśli nie widzisz dynamicznego projektu sieci Web wymienionego jako dostępny projekt, wykonaj następujące czynności: Wybierz pozycję Plik, wybierz pozycję Nowy, wybierz pozycję Projekt..., rozwiń węzeł Sieć Web, wybierz pozycję Dynamiczny projekt sieci Web, a następnie wybierz przycisk Dalej.
Wprowadź nazwę projektu w polu Nazwa projektu, a następnie w menu rozwijanym Środowisko uruchomieniowe docelowe wybierz opcjonalnie wartość (np. Apache Tomcat v7.0), a następnie wybierz pozycję Zakończ. Wybranie docelowego środowiska uruchomieniowego umożliwia uruchamianie projektu lokalnie za pośrednictwem środowiska Eclipse.
W środowisku Eclipse w widoku Project Explorer (Eksplorator projektów) rozwiń projekt. Kliknij prawym przyciskiem myszy pozycję WebContent, wybierz pozycję Nowy, a następnie wybierz pozycję Plik JSP.
W oknie dialogowym New JSP File (Nowy plik JSP) nazwij plik index.jsp. Zachowaj folder nadrzędny jako WebContent, jak pokazano na poniższej ilustracji, a następnie wybierz przycisk Dalej.
W oknie dialogowym Wybieranie szablonu JSP na potrzeby tego samouczka wybierz pozycję Nowy plik JSP (html),a następnie wybierz pozycję Zakończ.
Po otwarciu pliku index.jsp w środowisku Eclipse dodaj tekst, aby wyświetlić element Hello World! w istniejącym
<body>
elemecie. Zaktualizowana zawartość elementu<body>
powinna wyglądać podobnie do następującego kodu:<body> <% out.println("Hello World!"); %> </body>
Zapisz plik index.jsp.
Jeśli w kroku 2 ustawisz docelowe środowisko uruchomieniowe, możesz wybrać pozycję Projekt, a następnie uruchomić aplikację JSP lokalnie:
Instalowanie zestawu Java SDK usługi SQL
Najprostszym sposobem pobrania zestawu SDK Java usługi SQL i jego zależności jest skorzystanie z usługi Apache Maven. W tym celu należy przekonwertować projekt na projekt Maven, wykonując następujące czynności:
Kliknij prawym przyciskiem myszy projekt w Eksploratorze projektów, wybierz pozycję Konfiguruj, wybierz polecenie Konwertuj na projekt Maven.
W oknie Tworzenie nowego modułu POM zaakceptuj wartości domyślne i wybierz pozycję Zakończ.
W widoku Project Explorer (Eksplorator projektów) otwórz plik pom.xml.
Na karcie Zależności w okienku Zależności wybierz pozycję Dodaj.
W oknie Select Dependency (Wybierz zależność) wykonaj następujące czynności:
- W polu Identyfikator grupy wprowadź .
com.azure
- W polu Identyfikator artefaktu wprowadź wartość
azure-cosmos
. - W polu Wersja wprowadź wartość
4.11.0
.
Możesz również dodać kod XML zależności dla identyfikatora grupy i identyfikatora artefaktu bezpośrednio do pliku pom.xml :
<dependency> <groupId>com.azure</groupId> <artifactId>azure-cosmos</artifactId> <version>4.11.0</version> </dependency>
- W polu Identyfikator grupy wprowadź .
Wybierz przycisk OK , a narzędzie Maven zainstaluje zestaw SDK języka Java SQL lub zapisze plik pom.xml.
Korzystanie z usługi Azure Cosmos DB w aplikacji Java
Teraz dodajmy modele, widoki i kontrolery do aplikacji internetowej.
Dodaj model
Najpierw zdefiniujmy model w nowym pliku TodoItem.java. Klasa TodoItem
definiuje schemat elementu wraz z metodami getter i setter:
package com.microsoft.azure.cosmos.sample.model;
//@Data
//@Builder
public class TodoItem {
private String entityType;
private String category;
private boolean complete;
private String id;
private String name;
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getEntityType() {
return entityType;
}
public void setEntityType(String entityType) {
this.entityType = entityType;
}
public boolean isComplete() {
return complete;
}
public void setComplete(boolean complete) {
this.complete = complete;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Dodawanie klas obiektów dostępu do danych (DAO)
Utwórz obiekt dostępu do danych (DAO), aby wyodrębnić utrwalanie elementów zadań do wykonania w usłudze Azure Cosmos DB. Aby zapisać zadania do wykonania w kolekcji, klient musi wiedzieć, w której bazie danych i kolekcji powinien je zachowywać (jak to jest wskazane przez linki do samego siebie). Ogólnie rzecz biorąc, najlepiej jest zapisać w pamięci podręcznej obiekty bazy danych i kolekcji, tam gdzie to jest możliwe, aby uniknąć dodatkowych połączeń do bazy danych.
Aby wywołać usługę Azure Cosmos DB, należy utworzyć wystąpienie nowego
cosmosClient
obiektu. Ogólnie rzecz biorąc, najlepszym rozwiązaniem jest ponowne użyciecosmosClient
obiektu, a nie konstruowanie nowego klienta dla każdego kolejnego żądania. Klienta można ponownie użyć, definiując go wcosmosClientFactory
klasie . Zaktualizuj wartości HOST i MASTER_KEY zapisane w kroku 1. Zastąp zmienną HOST identyfikatorem URI i zastąp MASTER_KEY kluczem PODSTAWOWYM. Użyj następującego kodu, aby utworzyć klasęCosmosClientFactory
w pliku CosmosClientFactory.java :package com.microsoft.azure.cosmos.sample.dao; import com.azure.cosmos.ConsistencyLevel; import com.azure.cosmos.CosmosClient; import com.azure.cosmos.CosmosClientBuilder; public class CosmosClientFactory { private static final String HOST = "[ACCOUNT HOST NAME]"; private static final String MASTER_KEY = "[ACCOUNT KEY]"; private static CosmosClient cosmosClient = new CosmosClientBuilder() .endpoint(HOST) .key(MASTER_KEY) .consistencyLevel(ConsistencyLevel.EVENTUAL) .buildClient(); public static CosmosClient getCosmosClient() { return cosmosClient; } }
Utwórz nowy plik TodoDao.java i dodaj klasę
TodoDao
do tworzenia, aktualizowania, odczytywania i usuwania elementów zadań do wykonania: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); }
Utwórz nowy plik MockDao.java i dodaj klasę
MockDao
. Ta klasa implementujeTodoDao
klasę w celu wykonywania operacji CRUD na elementach: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(); } }
Utwórz nowy plik DocDbDao.java i dodaj klasę
DocDbDao
. Ta klasa definiuje kod, aby utrwały element TodoItems do kontenera, pobiera bazę danych i kolekcję, jeśli istnieje, lub tworzy nowy, jeśli nie istnieje. W tym przykładzie użyto formatu Gson do serializacji i anulowania serializacji dokumentów JSON todoItem Plain Old Java Objects (POJO) do dokumentów JSON. Aby zapisać zadania do wykonania w kolekcji, klient musi wiedzieć, w której bazie danych i kolekcji powinien je zachowywać (jak to jest wskazane przez linki do samego siebie). Ta klasa definiuje również funkcję pomocnika w celu pobrania dokumentów przez inny atrybut (np. "ID") zamiast linku samodzielnego. Możesz użyć metody pomocniczej, aby pobrać dokument JSON TodoItem według identyfikatora, a następnie deserializować go do poJO.Możesz również użyć
cosmosClient
obiektu klienta, aby uzyskać kolekcję lub listę zadań do wykonania przy użyciu zapytania SQL. Na koniec zdefiniujesz metodę delete, aby usunąć element TodoItem z listy. Poniższy kod przedstawia zawartośćDocDbDao
klasy: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; } } }
Następnie utwórz nowy plik TodoDaoFactory.java i dodaj
TodoDaoFactory
klasę, która tworzy nowy obiekt DocDbDao:package com.microsoft.azure.cosmos.sample.dao; public class TodoDaoFactory { private static TodoDao myTodoDao = new DocDbDao(); public static TodoDao getDao() { return myTodoDao; } }
Dodawanie kontrolera
Dodaj kontroler TodoItemController do aplikacji. W tym projekcie użyto Projektu Lombok, aby wygenerować konstruktor, metody pobierające, metody ustawiające i konstruktora. Alternatywnie możesz napisać ten kod ręcznie lub wygenerować go w środowisku IDE:
package com.microsoft.azure.cosmos.sample.controller;
import java.util.List;
import java.util.UUID;
import lombok.NonNull;
import com.microsoft.azure.cosmos.sample.dao.TodoDao;
import com.microsoft.azure.cosmos.sample.dao.TodoDaoFactory;
import com.microsoft.azure.cosmos.sample.model.TodoItem;
public class TodoItemController {
public static TodoItemController getInstance() {
if (todoItemController == null) {
todoItemController = new TodoItemController(TodoDaoFactory.getDao());
}
return todoItemController;
}
private static TodoItemController todoItemController;
private final TodoDao todoDao;
TodoItemController(TodoDao todoDao) {
this.todoDao = todoDao;
}
public TodoItem createTodoItem(@NonNull String name,
@NonNull String category, boolean isComplete) {
TodoItem todoItem = new TodoItem();
todoItem.setName(name);
todoItem.setCategory(category);
todoItem.setComplete(isComplete);
todoItem.setId(UUID.randomUUID().toString());
return todoDao.createTodoItem(todoItem);
}
public boolean deleteTodoItem(@NonNull String id) {
return todoDao.deleteTodoItem(id);
}
public TodoItem getTodoItemById(@NonNull String id) {
return todoDao.readTodoItem(id);
}
public List<TodoItem> getTodoItems() {
return todoDao.readTodoItems();
}
public TodoItem updateTodoItem(@NonNull String id, boolean isComplete) {
return todoDao.updateTodoItem(id, isComplete);
}
}
Tworzenie serwletu
Następnie utwórz serwlet do kierowania żądań HTTP do kontrolera. Utwórz plik ApiServlet.java i zdefiniuj pod nim następujący kod:
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);
}
}
Łączenie pozostałej części aplikacji Java
Teraz, po zakończeniu zabawy bitów, wszystko, co pozostało, to utworzyć szybki interfejs użytkownika i połączyć go z dao.
Do wyświetlenia użytkownikowi potrzebny jest internetowy interfejs użytkownika. Napiszmy ponownie index.jsp , który utworzyliśmy wcześniej za pomocą następującego kodu:
<%@ 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>
Na koniec napisz kod JavaScript po stronie klienta, aby powiązać internetowy interfejs użytkownika i serwlet ze sobą:
/** * 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(); });
Teraz pozostało już tylko przetestować aplikację. Uruchom aplikację lokalnie, a następnie dodaj jakieś zadania do wykonania, wpisując nazwę zadania oraz jego kategorię i klikając przycisk Add Task (Dodaj zadanie). Po pojawieniu się elementu możesz zaktualizować, czy zostało ono ukończone, przełączając pole wyboru i klikając pozycję Aktualizuj zadania.
Wdrożenie aplikacji w języku Java w usłudze Azure Web Sites
Usługa Azure Web Sites sprawia, że wdrożenie aplikacji Java sprowadza się do wyeksportowania aplikacji jako pliku WAR i przesłania go na serwer za pomocą systemu kontroli źródła (np. Git) lub FTP.
Aby wyeksportować aplikację jako plik WAR, kliknij prawym przyciskiem myszy projekt w Eksploratorze projektów, wybierz polecenie Eksportuj, a następnie wybierz pozycję PLIK WAR.
W oknie WAR Export (Eksport pliku WAR) wykonaj następujące czynności:
- W polu Projekt internetowy wprowadź ciąg azure-cosmos-java-sample.
- W polu Miejsce docelowe wybierz miejsce zapisania pliku WAR.
- Wybierz Zakończ.
Teraz, gdy masz już plik WAR, możesz go po prostu przesłać do katalogu webapps swojej usługi Azure Web Sites. Aby uzyskać instrukcje dotyczące przekazywania pliku, zobacz Add a Java application to Azure App Service Web Apps (Dodawanie aplikacji Java do usługi Web Apps w usłudze Azure App Service). Po przekazaniu pliku WAR do katalogu aplikacji internetowych środowisko uruchomieniowe wykryje, że został dodany i automatycznie go załaduje.
Aby wyświetlić gotową aplikację, przejdź do
http://YOUR\_SITE\_NAME.azurewebsites.net/azure-cosmos-java-sample/
i zacznij dodawać zadania.
Pobieranie projektu z usługi GitHub
Wszystkie przykłady w tym samouczku są zawarte w projekcie todo w usłudze GitHub. Aby zaimportować projekt todo do środowiska Eclipse, upewnij się, że dysponujesz oprogramowaniem i zasobami wymienionymi w sekcji Wymagania wstępne, a następnie wykonaj następujące czynności:
Zainstaluj Projekt Lombok. Lombok służy w projekcie do generowania konstruktorów, metod pobierających i ustawiających. Po pobraniu pliku lombok.jar kliknij go dwukrotnie, aby go zainstalować, lub zainstaluj go przy użyciu wiersza polecenia.
Jeśli środowisko Eclipse jest otwarte, zamknij je i uruchom ponownie, aby załadować Lombok.
W środowisku Eclipse w menu Plik wybierz pozycję Importuj.
W oknie Importowanie wybierz pozycję Git, wybierz pozycję Projekty z usługi Git, a następnie wybierz pozycję Dalej.
Na ekranie Wybieranie źródła repozytorium wybierz pozycję Klonuj identyfikator URI.
Na ekranie Źródłowe repozytorium Git w polu Identyfikator URI wprowadź ciąg https://github.com/Azure-Samples/azure-cosmos-java-sql-api-todo-app, a następnie wybierz przycisk Dalej.
Na ekranie Wybór gałęzi upewnij się, że wybrano pozycję main , a następnie wybierz przycisk Dalej.
Na ekranie Miejsce docelowe lokalne wybierz pozycję Przeglądaj , aby wybrać folder, w którym można skopiować repozytorium, a następnie wybierz przycisk Dalej.
Na ekranie Wybieranie kreatora do użycia do importowania projektów upewnij się, że wybrano opcję Importuj istniejące projekty , a następnie wybierz przycisk Dalej.
Na ekranie Importowanie projektów usuń zaznaczenie projektu DocumentDB , a następnie wybierz pozycję Zakończ. Projekt DocumentDB zawiera zestaw SDK Java usługi Azure Cosmos DB, który zamiast tego zostanie dodany jako zależność.
W Eksploratorze projektów przejdź do pozycji azure-cosmos-java-sample\src\com.microsoft.azure.cosmos.sample.dao\DocumentClientFactory.java i zastąp wartości HOST i MASTER_KEY identyfikatorem URI i KLUCZEM PODSTAWOWYM dla konta usługi Azure Cosmos DB, a następnie zapisz plik. Aby uzyskać więcej informacji, zobacz Krok 1. Utwórz konto bazy danych usługi Azure Cosmos DB.
W Eksploratorze projektów kliknij prawym przyciskiem myszy pozycję azure-cosmos-java-sample, wybierz pozycję Ścieżka kompilacji, a następnie wybierz polecenie Konfiguruj ścieżkę kompilacji.
Na ekranie Ścieżka kompilacji języka Java w okienku po prawej stronie wybierz kartę Biblioteki, a następnie wybierz pozycję Dodaj zewnętrzne reguły JAR. Przejdź do lokalizacji pliku lombok.jar, a następnie wybierz pozycję Otwórz, a następnie wybierz przycisk OK.
Użyj kroku 12, aby ponownie otworzyć okno Właściwości , a następnie w okienku po lewej stronie wybierz pozycję Docelowe środowiska uruchomieniowe.
Na ekranie Docelowe środowiska uruchomieniowe wybierz pozycję Nowe, wybierz pozycję Apache Tomcat v7.0, a następnie wybierz przycisk OK.
Użyj kroku 12, aby ponownie otworzyć okno Właściwości , a następnie w okienku po lewej stronie wybierz pozycję Aspekty projektu.
Na ekranie Project Facets (Aspekty projektu) wybierz pozycję Dynamic Web Module (Dynamiczny moduł internetowy) i Java (Java), a następnie wybierz przycisk OK.
Na karcie Serwery w dolnej części ekranu kliknij prawym przyciskiem myszy pozycję Serwer Tomcat v7.0 na hoście lokalnym, a następnie wybierz polecenie Dodaj i usuń.
W oknie Dodawanie i usuwanie przenieś pozycję azure-cosmos-java-sample do pola Skonfigurowane, a następnie wybierz pozycję Zakończ.
Na karcie Serwery kliknij prawym przyciskiem myszy serwer Tomcat v7.0 na hoście lokalnym, a następnie wybierz polecenie Uruchom ponownie.
W przeglądarce przejdź do
http://localhost:8080/azure-cosmos-java-sample/
i zacznij dodawać zadania do listy zadań. Należy pamiętać, że jeśli zmieniono domyślne wartości portów, zmień 8080 na wybraną wartość.Aby wdrożyć projekt w witrynie internetowej platformy Azure, zobacz Krok 6. Wdróż aplikację w witrynach internetowych platformy Azure.
Następne kroki
Próbujesz zaplanować pojemność migracji do usługi Azure Cosmos DB? Informacje o istniejącym klastrze bazy danych można użyć do planowania pojemności.
- Jeśli wszystko, co wiesz, to liczba rdzeni wirtualnych i serwerów w istniejącym klastrze bazy danych, przeczytaj o szacowaniu jednostek żądań przy użyciu rdzeni wirtualnych lub procesorów wirtualnych
- Jeśli znasz typowe stawki żądań dla bieżącego obciążenia bazy danych, przeczytaj o szacowaniu jednostek żądań przy użyciu planisty pojemności usługi Azure Cosmos DB