教程:使用 Azure Cosmos DB 和 SQL API 构建 Java Web 应用程序Tutorial: Build a Java web application using Azure Cosmos DB and the SQL API

本 Java Web 应用程序教程介绍了如何使用 Azure Cosmos DB 服务通过 Azure 应用服务 Web 应用上托管的 Java 应用程序来存储和访问数据。This Java web application tutorial shows you how to use the Azure Cosmos DB service to store and access data from a Java application hosted on Azure App Service Web Apps. 本文介绍:In this article, you will learn:

  • 如何在 Eclipse 中构建基本 JavaServer Pages (JSP) 程序。How to build a basic JavaServer Pages (JSP) application in Eclipse.
  • 如何使用 Azure Cosmos DB Java SDK 来处理 Azure Cosmos DB 服务。How to work with the Azure Cosmos DB service using the Azure Cosmos DB Java SDK.

此 Java 应用程序教程演示了如何创建一个基于 web 的任务管理应用程序,可以使用此应用程序创建、检索任务,以及将任务标记为已完成,如下图所示。This Java application tutorial shows you how to create a web-based task-management application that enables you to create, retrieve, and mark tasks as complete, as shown in the following image. ToDo 列表中的每个任务都存储为 Azure Cosmos DB 中的 JSON 文档。Each of the tasks in the ToDo list is stored as JSON documents in Azure Cosmos DB.

我的 ToDo 列表 Java 应用程序

提示

本应用程序开发教程假设读者之前有使用 Java 的经验。This application development tutorial assumes that you have prior experience using Java. 如果不熟悉 Java 或必备工具,建议从 GitHub 下载完整的 todo 项目,并按照本文末尾的说明生成该项目。If you are new to Java or the prerequisite tools, we recommend downloading the complete todo project from GitHub and building it using the instructions at the end of this article. 构建之后,可以回顾本文以深入了解项目上下文中的代码。Once you have it built, you can review the article to gain insight on the code in the context of the project.

此 Java Web 应用程序教程的先决条件Prerequisites for this Java web application tutorial

在开始本应用程序开发教程前,必须具有:Before you begin this application development tutorial, you must have the following:

如果是首次安装这些工具,那么可以参考 coreservlets.com 网站的教程:安装 TomCat7 并将其与 Eclipse 一起使用一文的“快速入门”部分提供的安装过程进行演练。If you're installing these tools for the first time, coreservlets.com provides a walk-through of the installation process in the Quick Start section of their Tutorial: Installing TomCat7 and Using it with Eclipse article.

步骤 1:创建 Azure Cosmos DB 帐户Step 1: Create an Azure Cosmos DB account

首先创建一个 Azure Cosmos DB 帐户。Let's start by creating an Azure Cosmos DB account. 如果已有一个帐户,或者要在本教程中使用 Azure Cosmos DB 模拟器,可以跳到步骤 2:创建 Java JSP 应用程序If you already have an account or if you are using the Azure Cosmos DB Emulator for this tutorial, you can skip to Step 2: Create the Java JSP application.

  1. 转到 Azure 门户以创建 Azure Cosmos DB 帐户。Go to the Azure portal to create an Azure Cosmos DB account. 搜索“Azure Cosmos DB”,然后选择它。 。Search for and select Azure Cosmos DB.

    Azure 门户“数据库”窗格

  2. 选择“添加” 。Select Add.

  3. 在“创建 Azure Cosmos DB 帐户”页上,输入新 Azure Cosmos 帐户的基本设置 。On the Create Azure Cosmos DB Account page, enter the basic settings for the new Azure Cosmos account.

    设置Setting Value DescriptionDescription
    订阅Subscription 订阅名称Subscription name 选择要用于此 Azure Cosmos 帐户的 Azure 订阅。Select the Azure subscription that you want to use for this Azure Cosmos account.
    资源组Resource Group 资源组名称Resource group name 选择一个资源组,或者选择“新建”,然后输入新资源组的唯一名称。 Select a resource group, or select Create new, then enter a unique name for the new resource group.
    帐户名Account Name 唯一的名称A unique name 输入标识此 Azure Cosmos 帐户的名称。Enter a name to identify your Azure Cosmos account. 由于 documents.azure.cn 字符串将追加到所提供的 ID 后面以创建 URI,因此,请使用唯一的 ID。Because documents.azure.cn is appended to the ID that you provide to create your URI, use a unique ID.

    ID 只能包含小写字母、数字和连字符 (-) 字符。The ID can only contain lowercase letters, numbers, and the hyphen (-) character. 它的长度必须介于 3 到 31 个字符之间。It must be between 3-31 characters in length.
    APIAPI 要创建的帐户的类型The type of account to create 选择“Core (SQL)”,以便使用 SQL 语法创建文档数据库并进行查询 。Select Core (SQL) to create a document database and query by using SQL syntax.

    API 确定要创建的帐户的类型。The API determines the type of account to create. Azure Cosmos DB 提供五种 API:适用于文档数据的 Core (SQL) 和 MongoDB、适用于图形数据的 Gremlin、Azure 表和 Cassandra。Azure Cosmos DB provides five APIs: Core (SQL) and MongoDB for document data, Gremlin for graph data, Azure Table, and Cassandra. 目前,你必须为每种 API 创建单独的帐户。Currently, you must create a separate account for each API.

    详细了解 SQL APILearn more about the SQL API.
    位置Location 离用户最近的区域The region closest to your users 选择用于托管 Azure Cosmos DB 帐户的地理位置。Select a geographic location to host your Azure Cosmos DB account. 使用离用户最近的位置,使他们能够以最快的速度访问数据。Use the location that is closest to your users to give them the fastest access to the data.

    Azure Cosmos DB 的“新建帐户”页

  4. 选择“查看 + 创建” 。Select Review + create. 可以跳过“网络”和“标记”部分 。You can skip the Network and Tags sections.

  5. 检查帐户设置,然后选择“创建”。 Review the account settings, and then select Create. 创建帐户需要几分钟时间。It takes a few minutes to create the account. 等待门户页显示“你的部署已完成” 消息。Wait for the portal page to display Your deployment is complete.

    Azure 门户“通知”窗格

  6. 选择“转到资源”,转到 Azure Cosmos DB 帐户页。 Select Go to resource to go to the Azure Cosmos DB account page.

    Azure Cosmos DB 帐户页

转到 Azure Cosmos DB 帐户页,并选择“密钥” 。Go to the Azure Cosmos DB account page, and select Keys. 复制要在下一步创建的 Web 应用程序中使用的值。Copy the values to use in the web application you create next.

Azure 门户的 Azure Cosmos DB 帐户页上突出显示“密钥”按钮的屏幕截图

步骤 2:创建 Java JSP 应用程序Step 2: Create the Java JSP application

若要创建 JSP 应用程序,请执行以下步骤:To create the JSP application:

  1. 首先,创建一个 Java 项目。First, we'll start off by creating a Java project. 启动 Eclipse,依次单击“文件”、“新建”和“动态 Web 项目” 。Start Eclipse, then click File, click New, and then click Dynamic Web Project. 如果未看到“动态 Web 项目”作为可用项目列出,请执行下列操作:依次单击“文件”、“新建”和“项目”,展开“Web”,并依次单击“动态 Web 项目”、“下一步” 。If you don't see Dynamic Web Project listed as an available project, do the following: click File, click New, click Project…, expand Web, click Dynamic Web Project, and click Next.

    JSP Java 应用程序开发

  2. 在“项目名称”框中输入项目名称,在“目标运行时”下拉菜单中随意选择一个值(例如 Apache Tomcat v7.0),并单击“完成” 。Enter a project name in the Project name box, and in the Target Runtime drop-down menu, optionally select a value (e.g. Apache Tomcat v7.0), and then click Finish. 选择目标运行可通过 Eclipse 在本地运行项目。Selecting a target runtime enables you to run your project locally through Eclipse.

  3. 在 Eclipse 的项目资源管理器视图中,展开项目。In Eclipse, in the Project Explorer view, expand your project. 右键单击“WebContent”,并依次单击“新建”、“JSP 文件” 。Right-click WebContent, click New, and then click JSP File.

  4. 在“新建 JSP 文件”对话框中,将文件命名为“index.jsp” 。In the New JSP File dialog box, name the file index.jsp. 将父文件夹保留为“WebContent”,如下图所示,并单击“下一步” 。Keep the parent folder as WebContent, as shown in the following illustration, and then click Next.

    创建新的 JSP 文件 - Java Web 应用程序教程

  5. 对于本教程,请在“选择 JSP 模板”对话框中选择“新建 JSP 文件(html)”,并单击“完成” 。In the Select JSP Template dialog box, for the purpose of this tutorial select New JSP File (html), and then click Finish.

  6. 在 Eclipse 中打开 index.jsp 文件后,添加文本以在以下元素中显示“Hello World!” :When the index.jsp file opens in Eclipse, add text to display Hello World! 在现有 <body> 元素中。within the existing <body> element. 更新后的 <body> 内容应类似于以下代码:The updated <body> content should look like the following code:

     <body>
         <% out.println("Hello World!"); %>
     </body>
    
  7. 保存 index.jsp 文件。Save the index.jsp file.

  8. 如果在步骤 2 中设置了目标运行时,则可以依次单击“项目”、“运行”,在本地运行 JSP 应用程序: If you set a target runtime in step 2, you can click Project and then Run to run your JSP application locally:

    Hello World — Java 应用程序教程

步骤 3:安装 SQL Java SDKStep 3: Install the SQL Java SDK

提取 SQL Java SDK 及其依赖项的最简单方法是使用 Apache MavenThe easiest way to pull in the SQL Java SDK and its dependencies is through Apache Maven.

要执行此操作,需要通过完成以下步骤将项目转换为 maven 项目:To do this, you will need to convert your project to a maven project by completing the following steps:

  1. 在项目资源管理器中右键单击项目,单击“配置”,并单击“转换为 Maven 项目”。 Right-click your project in the Project Explorer, click Configure, click Convert to Maven Project.

  2. 在“创建新 POM”窗口中,接受默认值,并单击“完成”。 In the Create new POM window, accept the defaults, and click Finish.

  3. 在“项目资源管理器” 中,打开 pom.xml 文件。In Project Explorer, open the pom.xml file.

  4. 在“依赖项”选项卡上,在“依赖项”窗格中单击“添加” 。On the Dependencies tab, in the Dependencies pane, click Add.

  5. 在“选择依赖项” 窗口中,执行以下操作:In the Select Dependency window, do the following:

    • 在“组 ID” 框中,输入 com.microsoft.azure。In the Group Id box, enter com.microsoft.azure.

    • 在“项目 ID” 框中输入 azure-documentdb。In the Artifact Id box, enter azure-documentdb.

    • 在“版本” 框中输入 1.5.1。In the Version box, enter 1.5.1.

      安装 SQL Java 应用程序 SDK

    • 或者,通过文本编辑器直接将组 ID 和项目 ID 的依赖项 XML 添加到 pom.xml:Or add the dependency XML for Group Id and Artifact Id directly to the pom.xml via a text editor:

      <dependency>
          <groupId>com.microsoft.azure</groupId>
          <artifactId>azure-documentdb</artifactId>
          <version>1.9.1</version>
      </dependency>
      
  6. 单击“确定” ,Maven 将安装 SQL Java SDK。Click OK and Maven will install the SQL Java SDK.

  7. 保存 pom.xml 文件。Save the pom.xml file.

步骤 4:在 Java 应用程序中使用 Azure Cosmos DB 服务Step 4: Using the Azure Cosmos DB service in a Java application

  1. 首先,让我们在 TodoItem.java 中定义 TodoItem 对象:First, let's define the TodoItem object in TodoItem.java:

     @Data
     @Builder
     public class TodoItem {
         private String category;
         private boolean complete;
         private String id;
         private String name;
     }
    

    在此项目中,将使用项目 Lombok 生成构造函数、getter、setter 和一个生成器。In this project, you are using Project Lombok to generate the constructor, getters, setters, and a builder. 或者,可以手动编写此代码,或使用 IDE 生成此代码。Alternatively, you can write this code manually or have the IDE generate it.

  2. 若要调用 Azure Cosmos DB 服务,必须实例化一个新的 DocumentClient 。To invoke the Azure Cosmos DB service, you must instantiate a new DocumentClient. 一般情况下,最好是重用 DocumentClient -而不是为每个后续请求构造新的客户端。In general, it is best to reuse the DocumentClient - rather than construct a new client for each subsequent request. 我们可以通过在 DocumentClientFactory中包装客户端来重用此客户端。We can reuse the client by wrapping the client in a DocumentClientFactory. 在 DocumentClientFactory.java 中,需要在此处粘贴 步骤 1 中保存到剪贴板中的 URI 和 PRIMARY KEY 值。In DocumentClientFactory.java, you need to paste the URI and PRIMARY KEY value you saved to your clipboard in step 1. 将 [YOUR_ENDPOINT_HERE] 替换为 URI,将 [YOUR_KEY_HERE] 替换为主密钥。Replace [YOUR_ENDPOINT_HERE] with your URI and replace [YOUR_KEY_HERE] with your PRIMARY KEY.

     private static final String HOST = "[YOUR_ENDPOINT_HERE]";
     private static final String MASTER_KEY = "[YOUR_KEY_HERE]";
    
     private static DocumentClient documentClient = new DocumentClient(HOST, MASTER_KEY,
                     ConnectionPolicy.GetDefault(), ConsistencyLevel.Session);
    
     public static DocumentClient getDocumentClient() {
         return documentClient;
     }
    
  3. 接下来创建数据访问对象 (DAO),将 ToDo 项保存到 Azure Cosmos DB 的过程进行抽象。Now let's create a Data Access Object (DAO) to abstract persisting our ToDo items to Azure Cosmos DB.

    要将 ToDo 项保存到集合中,客户端需要了解可用于保存的数据库和集合(通过自链接引用)。In order to save ToDo items to a collection, the client needs to know which database and collection to persist to (as referenced by self-links). 通常,如果可能的话最好缓存数据库和集合,以避免额外的往返访问数据库。In general, it is best to cache the database and collection when possible to avoid additional round-trips to the database.

    以下代码演示了存在数据库和集合的情况下如何检索数据库和集合,如果不存在,则创建新的数据库和集合。The following code illustrates how to retrieve our database and collection, if it exists, or create a new one if it doesn't exist:

     public class DocDbDao implements TodoDao {
         // The name of our database.
         private static final String DATABASE_ID = "TodoDB";
    
         // The name of our collection.
         private static final String COLLECTION_ID = "TodoCollection";
    
         // The Azure Cosmos DB Client
         private static DocumentClient documentClient = DocumentClientFactory
                 .getDocumentClient();
    
         // Cache for the database object, so we don't have to query for it to
         // retrieve self links.
         private static Database databaseCache;
    
         // Cache for the collection object, so we don't have to query for it to
         // retrieve self links.
         private static DocumentCollection collectionCache;
    
         private Database getTodoDatabase() {
             if (databaseCache == null) {
                 // Get the database if it exists
                 List<Database> databaseList = documentClient
                         .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 {
                         Database databaseDefinition = new Database();
                         databaseDefinition.setId(DATABASE_ID);
    
                         databaseCache = documentClient.createDatabase(
                                 databaseDefinition, null).getResource();
                     } catch (DocumentClientException 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 DocumentCollection getTodoCollection() {
             if (collectionCache == null) {
                 // Get the collection if it exists.
                 List<DocumentCollection> collectionList = documentClient
                         .queryCollections(
                                 getTodoDatabase().getSelfLink(),
                                 "SELECT * FROM root r WHERE r.id='" + COLLECTION_ID
                                         + "'", null).getQueryIterable().toList();
    
                 if (collectionList.size() > 0) {
                     // Cache the collection object so we won't have to query for it
                     // later to retrieve the selfLink.
                     collectionCache = collectionList.get(0);
                 } else {
                     // Create the collection if it doesn't exist.
                     try {
                         DocumentCollection collectionDefinition = new DocumentCollection();
                         collectionDefinition.setId(COLLECTION_ID);
    
                         collectionCache = documentClient.createCollection(
                                 getTodoDatabase().getSelfLink(),
                                 collectionDefinition, null).getResource();
                     } catch (DocumentClientException 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 collectionCache;
         }
     }
    
  4. 下一步是编写一些代码将 TodoItem 保存到集合中。The next step is to write some code to persist the TodoItems into the collection. 本示例使用 Gson 将 TodoItem 普通 Java 对象 (POJO) 序列化到 JSON 文档和从中反序列化 POJO。In this example, we will use Gson to serialize and de-serialize TodoItem Plain Old Java Objects (POJOs) to JSON documents.

     // We'll use Gson for POJO <=> JSON serialization for this example.
     private static Gson gson = new Gson();
    
     @Override
     public TodoItem createTodoItem(TodoItem todoItem) {
         // Serialize the TodoItem as a JSON Document.
         Document todoItemDocument = new Document(gson.toJson(todoItem));
    
         // Annotate the document as a TodoItem for retrieval (so that we can
         // store multiple entity types in the collection).
         todoItemDocument.set("entityType", "todoItem");
    
         try {
             // Persist the document using the DocumentClient.
             todoItemDocument = documentClient.createDocument(
                     getTodoCollection().getSelfLink(), todoItemDocument, null,
                     false).getResource();
         } catch (DocumentClientException e) {
             e.printStackTrace();
             return null;
         }
    
         return gson.fromJson(todoItemDocument.toString(), TodoItem.class);
     }
    
  5. 与 Azure Cosmos 数据库和集合一样,文档也是通过自链接来引用。Like Azure Cosmos databases and collections, documents are also referenced by self-links. 使用以下帮助程序函数可以通过另一个属性(例如“id”)来检索文档,而不是自链接:The following helper function lets us retrieve documents by another attribute (e.g. "id") rather than self-link:

     private Document getDocumentById(String id) {
         // Retrieve the document using the DocumentClient.
         List<Document> documentList = documentClient
                 .queryDocuments(getTodoCollection().getSelfLink(),
                         "SELECT * FROM root r WHERE r.id='" + id + "'", null)
                 .getQueryIterable().toList();
    
         if (documentList.size() > 0) {
             return documentList.get(0);
         } else {
             return null;
         }
     }
    
  6. 我们可使用步骤 5 中的帮助器方法按 ID 检索 TodoItem JSON 文档,然后将其反序列化到 POJO:We can use the helper method in step 5 to retrieve a TodoItem JSON document by id and then deserialize it to a POJO:

     @Override
     public TodoItem readTodoItem(String id) {
         // Retrieve the document by id using our helper method.
         Document todoItemDocument = getDocumentById(id);
    
         if (todoItemDocument != null) {
             // De-serialize the document in to a TodoItem.
             return gson.fromJson(todoItemDocument.toString(), TodoItem.class);
         } else {
             return null;
         }
     }
    
  7. 我们还可以通过 DocumentClient 使用 SQL 获取 TodoItem 的集合或列表:We can also use the DocumentClient to get a collection or list of TodoItems using SQL:

     @Override
     public List<TodoItem> readTodoItems() {
         List<TodoItem> todoItems = new ArrayList<TodoItem>();
    
         // Retrieve the TodoItem documents
         List<Document> documentList = documentClient
                 .queryDocuments(getTodoCollection().getSelfLink(),
                         "SELECT * FROM root r WHERE r.entityType = 'todoItem'",
                         null).getQueryIterable().toList();
    
         // De-serialize the documents in to TodoItems.
         for (Document todoItemDocument : documentList) {
             todoItems.add(gson.fromJson(todoItemDocument.toString(),
                     TodoItem.class));
         }
    
         return todoItems;
     }
    
  8. 使用 DocumentClient 更新文档的方法有多种。There are many ways to update a document with the DocumentClient. 在 Todo 列表应用程序中,我们希望能够切换 TodoItem 是否已完成。In our Todo list application, we want to be able to toggle whether a TodoItem is complete. 这可以通过更新文档中的"完成"属性来实现:This can be achieved by updating the "complete" attribute within the document:

     @Override
     public TodoItem updateTodoItem(String id, boolean isComplete) {
         // Retrieve the document from the database
         Document todoItemDocument = 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.
         todoItemDocument.set("complete", isComplete);
    
         try {
             // Persist/replace the updated document.
             todoItemDocument = documentClient.replaceDocument(todoItemDocument,
                     null).getResource();
         } catch (DocumentClientException e) {
             e.printStackTrace();
             return null;
         }
    
         return gson.fromJson(todoItemDocument.toString(), TodoItem.class);
     }
    
  9. 最后,我们希望能够从我们的列表中删除 TodoItem。Finally, we want the ability to delete a TodoItem from our list. 要执行此操作,我们可以使用之前编写的帮助器方法检索自链接,然后告诉客户端将其删除:To do this, we can use the helper method we wrote earlier to retrieve the self-link and then tell the client to delete it:

     @Override
     public boolean deleteTodoItem(String id) {
         // Azure Cosmos DB refers to documents by self link rather than id.
    
         // Query for the document to retrieve the self link.
         Document todoItemDocument = getDocumentById(id);
    
         try {
             // Delete the document by self link.
             documentClient.deleteDocument(todoItemDocument.getSelfLink(), null);
         } catch (DocumentClientException e) {
             e.printStackTrace();
             return false;
         }
    
         return true;
     }
    

步骤 5:将剩余的 Java 应用程序开发项目绑定到一起Step 5: Wiring the rest of the of Java application development project together

现在,我们完成了有趣的部分,剩下所有要做的是构建一个快速的用户接口,并将其与我们的 DAO 进行绑定。Now that we've finished the fun bits - all that's left is to build a quick user interface and wire it up to our DAO.

  1. 首先,让我们生成控制器以调用 DAO:First, let's start with building a controller to call our DAO:

     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 = TodoItem.builder().name(name).category(category)
                     .complete(isComplete).build();
             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);
         }
     }
    

    在更复杂的应用程序中,控制器可以包含基于 DAO 的复杂的业务逻辑。In a more complex application, the controller may house complicated business logic on top of the DAO.

  2. 接下来,我们将创建 servlet 将 HTTP 请求路由到控制器:Next, we'll create a servlet to route HTTP requests to the controller:

     public class TodoServlet 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);
             boolean isComplete = StringUtils.equalsIgnoreCase("true",
                     request.getParameter(TODO_ITEM_COMPLETE)) ? true : false;
    
             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.getWriter().println(apiResponse);
         }
    
         @Override
         protected void doPost(HttpServletRequest request,
                 HttpServletResponse response) throws ServletException, IOException {
             doGet(request, response);
         }
     }
    
  3. 我们需要一个 Web 用户界面来向用户显示。We'll need a web user interface to display to the user. 让我们重新编写之前创建的 index.jsp:Let's re-write the index.jsp we created earlier:

    <html>
    <head>
      <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
      <meta http-equiv="X-UA-Compatible" content="IE=edge;" />
      <title>Azure Cosmos DB 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>
    
  4. 最后,编写一些客户端 Javascript 将 Web 用户界面和 servlet 绑定在一起:And finally, write some client-side JavaScript to tie the web user interface and the servlet together:

     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');
    
           // Toggle 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();
     });
    
  5. 非常好!Awesome! 现在剩下的就是测试此应用程序。Now all that's left is to test the application. 在本地运行此应用程序,并添加一些 Todo 项,方法是填充项名称和类别,并单击“添加任务” 。Run the application locally, and add some Todo items by filling in the item name and category and clicking Add Task.

  6. 显示项之后,用户可以通过切换复选框,并单击“更新任务” 来更新项是否已完成。Once the item appears, you can update whether it's complete by toggling the checkbox and clicking Update Tasks.

步骤 6:将 Java 应用程序部署到 Azure 网站Step 6: Deploy your Java application to Azure Web Sites

要在 Azure 网站上部署 Java 应用程序,只需将应用程序导出为 WAR 文件,然后通过源控件(例如 Git)或 FTP 上传此文件。Azure Web Sites makes deploying Java applications as simple as exporting your application as a WAR file and either uploading it via source control (e.g. Git) or FTP.

  1. 要将应用程序导出为 WAR 文件,请在“项目资源管理器”中右键单击项目,然后依次单击“导出”和“WAR 文件”。 To export your application as a WAR file, right-click on your project in Project Explorer, click Export, and then click WAR File.

  2. 在“WAR 导出” 窗口中,执行以下操作:In the WAR Export window, do the following:

    • 在“Web 项目”框中,输入 azure-documentdb-java-sample。In the Web project box, enter azure-documentdb-java-sample.
    • 在“目标”框中,选择一个目标以保存 WAR 文件。In the Destination box, choose a destination to save the WAR file.
    • 单击“完成” 。Click Finish.
  3. 现在已经具有 WAR 文件,只需将它上传到 Azure 网站的 webapps 目录。Now that you have a WAR file in hand, you can simply upload it to your Azure Web Site's webapps directory. 有关上传此文件的说明,请参阅将 Java 应用程序添加到 Azure 应用服务 Web 应用For instructions on uploading the file, see Add a Java application to Azure App Service Web Apps.

    将 WAR 文件上传到 webapps 目录之后,运行时环境将检测到已经添加了此文件,并会自动加载它。Once the WAR file is uploaded to the webapps directory, the runtime environment will detect that you've added it and will automatically load it.

  4. 若要查看已完成的产品,请导航到 http://YOUR\_SITE\_NAME.chinacloudsites.cn/azure-java-sample/ 并开始添加任务!To view your finished product, navigate to http://YOUR\_SITE\_NAME.chinacloudsites.cn/azure-java-sample/ and start adding your tasks!

从 GitHub 获取项目Get the project from GitHub

GitHub 上的 todo 项目包含本教程中的所有示例。All the samples in this tutorial are included in the todo project on GitHub. 如果要将 todo 项目导入 Eclipse,请确保用户具有 先决条件 部分中所列的软件和资源,并执行以下操作:To import the todo project into Eclipse, ensure you have the software and resources listed in the Prerequisites section, then do the following:

  1. 安装 项目 LombokInstall Project Lombok. Lombok 用于生成项目中的构造函数、getter 和 setter。Lombok is used to generate constructors, getters, setters in the project. 下载 lombok.jar 文件之后,双击此文件进行安装,或者从命令行安装。Once you have downloaded the lombok.jar file, double-click it to install it or install it from the command line.
  2. 如果 Eclipse 处于打开状态,请将其关闭并重启以加载 Lombok。If Eclipse is open, close it and restart it to load Lombok.
  3. 在 Eclipse 中,在“文件”菜单上单击“导入” 。In Eclipse, on the File menu, click Import.
  4. 在“导入”窗口中,依次单击“Git”、“来自 Git 的项目”和“下一步” 。In the Import window, click Git, click Projects from Git, and then click Next.
  5. 在“选择存储库源”屏幕上,单击“克隆 URI”。 On the Select Repository Source screen, click Clone URI.
  6. 在“源 Git 存储库”屏幕上的“URI”框中,输入 https://github.com/Azure-Samples/documentdb-java-todo-app.git ,然后单击“下一步” 。On the Source Git Repository screen, in the URI box, enter https://github.com/Azure-Samples/documentdb-java-todo-app.git, and then click Next.
  7. 在“分支选择”屏幕上,确保已选择“master”,并单击“下一步”。 On the Branch Selection screen, ensure that master is selected, and then click Next.
  8. 在“本地目标”屏幕上,单击“浏览”选择要将存储库复制到的文件夹,并单击“下一步” 。On the Local Destination screen, click Browse to select a folder where the repository can be copied, and then click Next.
  9. 在“选择要用于导入项目的向导”屏幕上,确保已选择“导入现有项目”,并单击“下一步”。 On the Select a wizard to use for importing projects screen, ensure that Import existing projects is selected, and then click Next.
  10. 在“导入项目”屏幕上,取消选择“DocumentDB”项目,并单击“完成”。 On the Import Projects screen, unselect the DocumentDB project, and then click Finish. DocumentDB 项目包含 Azure Cosmos DB Java SDK,我们会将其添加为依赖项。The DocumentDB project contains the Azure Cosmos DB Java SDK, which we will add as a dependency instead.
  11. 在“项目资源管理器”中,导航到 azure-documentdb-java-sample\src\com.microsoft.azure.documentdb.sample.dao\DocumentClientFactory.java,并将 HOST 和 MASTER_KEY 值替换为 Azure Cosmos DB 帐户的 URI 和主密钥,然后保存该文件。 In Project Explorer, navigate to azure-documentdb-java-sample\src\com.microsoft.azure.documentdb.sample.dao\DocumentClientFactory.java and replace the HOST and MASTER_KEY values with the URI and PRIMARY KEY for your Azure Cosmos DB account, and then save the file. 有关详细信息,请参阅步骤 1.创建 Azure Cosmos 数据库帐户For more information, see Step 1. Create an Azure Cosmos database account.
  12. 在“项目资源管理器”中,右键单击“azure-documentdb-java-sample”,单击“生成路径”,并单击“配置生成路径”。 In Project Explorer, right-click the azure-documentdb-java-sample, click Build Path, and then click Configure Build Path.
  13. 在“Java 生成路径”屏幕上,在右侧窗格中,选择“库”选项卡,并单击“添加外部 JAR”。 On the Java Build Path screen, in the right pane, select the Libraries tab, and then click Add External JARs. 导航到 lombok.jar 文件的位置,并依次单击“打开”、“确定” 。Navigate to the location of the lombok.jar file, and click Open, and then click OK.
  14. 使用步骤 12 再次打开“属性”窗口,并在左窗格中单击“目标运行时” 。Use step 12 to open the Properties window again, and then in the left pane click Targeted Runtimes.
  15. 在“目标运行时”屏幕上,单击“新建”,选择“Apache Tomcat v7.0”,并单击“确定” 。On the Targeted Runtimes screen, click New, select Apache Tomcat v7.0, and then click OK.
  16. 使用步骤 12 再次打开“属性”窗口,并在左窗格中单击“项目方面” 。Use step 12 to open the Properties window again, and then in the left pane click Project Facets.
  17. 在“项目方面”屏幕上,选择“动态 Web 模块”和“Java”,并单击“确定” 。On the Project Facets screen, select Dynamic Web Module and Java, and then click OK.
  18. 在此屏幕下面的“服务器”选项卡上,右键单击“localhost 上的 Tomcat v7.0 服务器”,并单击“添加和删除” 。On the Servers tab at the bottom of the screen, right-click Tomcat v7.0 Server at localhost and then click Add and Remove.
  19. 在“添加和删除”窗口中,将 azure-documentdb-java-sample 移到“配置”框,然后单击“完成”。 On the Add and Remove window, move azure-documentdb-java-sample to the Configured box, and then click Finish.
  20. 在“服务器”选项卡上,右键单击“localhost 上的 Tomcat v7.0 服务器”,并单击“重新启动”。 In the Servers tab, right-click Tomcat v7.0 Server at localhost, and then click Restart.
  21. 在浏览器中,导航到 http://localhost:8080/azure-documentdb-java-sample/ 并开始向任务列表添加内容。In a browser, navigate to http://localhost:8080/azure-documentdb-java-sample/ and start adding to your task list. 请注意,如果更改了默认端口值,请将 8080 更改成选择的值。Note that if you changed your default port values, change 8080 to the value you selected.
  22. 要将项目部署到 Azure 网站,请参阅步骤 6. 将应用程序部署到 Azure 网站To deploy your project to an Azure web site, see Step 6. Deploy your application to Azure Web Sites.