快速入门:适用于 Java 的 Azure Cosmos DB for NoSQL 库

适用范围: NoSQL

开始使用适用于 Java 的 Azure Cosmos DB for NoSQL 客户端库来查询容器中的数据并对各个项执行常见操作。 请按照以下步骤,使用 Azure Developer CLI 将最小解决方案部署到环境。

API 参考文档 | 库源代码 | 包 (Maven) | Azure Developer CLI

先决条件

设置

将此项目的开发容器部署到环境中。 然后,使用 Azure Developer CLI (azd) 创建 Azure Cosmos DB for NoSQL 帐户并部署容器化示例应用程序。 示例应用程序使用客户端库来管理、创建、读取和查询示例数据。

Open in GitHub Codespaces

Open in Dev Container

重要

GitHub 帐户包括使用免费的存储和核心小时数的权利。 有关详细信息,请参阅包含的 GitHub 帐户存储和核心小时数

  1. 在项目的根目录中打开终端。

  2. 使用 azd auth login 向 Azure Developer CLI 进行身份验证。 按照该工具指定的步骤,使用首选 Azure 凭据向 CLI 进行身份验证。

    azd auth login
    
  3. 使用 azd init 来初始化项目。

    azd init
    
  4. 在初始化期间,配置唯一的环境名称。

    提示

    环境名称也将用作目标资源组名称。 对于本快速入门,请考虑使用 msdocs-cosmos-db-nosql

  5. 使用 azd up 部署 Azure Cosmos DB for NoSQL 帐户。 Bicep 模板还部署示例 Web 应用程序。

    azd up
    
  6. 在预配过程中,选择订阅和所需位置。 等待预配过程完成。 此过程可能需要大约 5 分钟

  7. 预配 Azure 资源后,输出中将包含指向正在运行的 Web 应用程序的 URL。

    Deploying services (azd deploy)
    
      (✓) Done: Deploying service web
    - Endpoint: <https://[container-app-sub-domain].azurecontainerapps.io>
    
    SUCCESS: Your application was provisioned and deployed to Azure in 5 minutes 0 seconds.
    
  8. 使用控制台中的 URL 在浏览器中导航到 Web 应用程序。 观察正在运行的应用的输出。

    Screenshot of the running web application.

安装客户端库

客户端库可以通过 Maven 作为 azure-spring-data-cosmos 包提供。

  1. 导航到 /src/web 文件夹并打开“pom.xml”文件。

  2. 如果它尚不存在,请为 azure-spring-data-cosmos 包添加一个条目。

    <dependency>
        <groupId>com.azure</groupId>
        <artifactId>azure-spring-data-cosmos</artifactId>
    </dependency>
    
  3. 此外,如果 azure-identity 包尚不存在,请为其添加另一个依赖项。

    <dependency>
        <groupId>com.azure</groupId>
        <artifactId>azure-identity</artifactId>
    </dependency>
    

对象模型

名称 描述
EnableCosmosRepositories 此类型是用于配置存储库以访问 Azure Cosmos DB for NoSQL 的方法修饰器。
CosmosRepository 此类是主客户端类,用于管理容器中的数据。
CosmosClientBuilder 此类是一个工厂,用于创建存储库所使用的客户端。
Query 此类型是一个方法修饰器,用于指定存储库执行的查询。

代码示例

模板中的示例代码使用名为 cosmicworks 的数据库和名为 products 的容器。 products 容器包含每个产品的名称、类别、数量、唯一标识符和销售标志等详细信息。 该容器使用 /category 属性作为逻辑分区键。

验证客户端

对大多数 Azure 服务的应用程序请求必须获得授权。 使用 DefaultAzureCredential 类型作为在应用程序和 Azure Cosmos DB for NoSQL 之间实现无密码连接的首选方式。 DefaultAzureCredential 支持多种身份验证方法,并确定应在运行时使用哪种方法。

重要

还可以直接使用密码、连接字符串或其他凭据授权对 Azure 服务的请求。 但是,应谨慎使用此方法。 开发人员必须尽量避免在不安全的位置公开这些机密。 任何可获得密码或密钥的人员都可向数据库服务进行身份验证。 DefaultAzureCredential 提供比帐户密钥更好的管理和安全优势,允许无密码身份验证,而不会有存储密钥的风险。

首先,此示例创建了一个继承自 AbstractCosmosConfiguration 的新类,以配置与 Azure Cosmos DB for NoSQL 的连接。

package com.microsoft.learn.azure.cosmosdb.nosql.quickstart;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.azure.cosmos.CosmosClientBuilder;
import com.azure.identity.DefaultAzureCredential;
import com.azure.identity.DefaultAzureCredentialBuilder;
import com.azure.spring.data.cosmos.config.AbstractCosmosConfiguration;
import com.azure.spring.data.cosmos.config.CosmosConfig;
import com.azure.spring.data.cosmos.repository.config.EnableCosmosRepositories;

// <repository_configuration>
@Configuration
@EnableCosmosRepositories
public class CosmosConfiguration extends AbstractCosmosConfiguration {
// </repository_configuration>

    @Value("#{environment.COSMOS_DB_ENDPOINT}")
    private String uri;

    // <create_client>
    @Bean
    public CosmosClientBuilder getCosmosClientBuilder() {
        DefaultAzureCredential azureTokenCredential = new DefaultAzureCredentialBuilder()
            .build();
            
        return new CosmosClientBuilder()
            .endpoint(uri)
            .credential(azureTokenCredential);
    }
    // </create_client>

    @Override
    public CosmosConfig cosmosConfig() {
        return CosmosConfig.builder()
            .enableQueryMetrics(true)
            .build();
    }

    // <get_database>
    @Override
    protected String getDatabaseName() {
        return "cosmicworks";
    }
    // </get_database>
}

在配置类中,此示例创建 CosmosClientBuilder 类的新实例,并使用 DefaultAzureCredential 实例配置身份验证。

package com.microsoft.learn.azure.cosmosdb.nosql.quickstart;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.azure.cosmos.CosmosClientBuilder;
import com.azure.identity.DefaultAzureCredential;
import com.azure.identity.DefaultAzureCredentialBuilder;
import com.azure.spring.data.cosmos.config.AbstractCosmosConfiguration;
import com.azure.spring.data.cosmos.config.CosmosConfig;
import com.azure.spring.data.cosmos.repository.config.EnableCosmosRepositories;

// <repository_configuration>
@Configuration
@EnableCosmosRepositories
public class CosmosConfiguration extends AbstractCosmosConfiguration {
// </repository_configuration>

    @Value("#{environment.COSMOS_DB_ENDPOINT}")
    private String uri;

    // <create_client>
    @Bean
    public CosmosClientBuilder getCosmosClientBuilder() {
        DefaultAzureCredential azureTokenCredential = new DefaultAzureCredentialBuilder()
            .build();
            
        return new CosmosClientBuilder()
            .endpoint(uri)
            .credential(azureTokenCredential);
    }
    // </create_client>

    @Override
    public CosmosConfig cosmosConfig() {
        return CosmosConfig.builder()
            .enableQueryMetrics(true)
            .build();
    }

    // <get_database>
    @Override
    protected String getDatabaseName() {
        return "cosmicworks";
    }
    // </get_database>
}

获取数据库

在配置类中,示例实现了一个方法,用于返回名为 cosmicworks 的现有数据库的名称。

package com.microsoft.learn.azure.cosmosdb.nosql.quickstart;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.azure.cosmos.CosmosClientBuilder;
import com.azure.identity.DefaultAzureCredential;
import com.azure.identity.DefaultAzureCredentialBuilder;
import com.azure.spring.data.cosmos.config.AbstractCosmosConfiguration;
import com.azure.spring.data.cosmos.config.CosmosConfig;
import com.azure.spring.data.cosmos.repository.config.EnableCosmosRepositories;

// <repository_configuration>
@Configuration
@EnableCosmosRepositories
public class CosmosConfiguration extends AbstractCosmosConfiguration {
// </repository_configuration>

    @Value("#{environment.COSMOS_DB_ENDPOINT}")
    private String uri;

    // <create_client>
    @Bean
    public CosmosClientBuilder getCosmosClientBuilder() {
        DefaultAzureCredential azureTokenCredential = new DefaultAzureCredentialBuilder()
            .build();
            
        return new CosmosClientBuilder()
            .endpoint(uri)
            .credential(azureTokenCredential);
    }
    // </create_client>

    @Override
    public CosmosConfig cosmosConfig() {
        return CosmosConfig.builder()
            .enableQueryMetrics(true)
            .build();
    }

    // <get_database>
    @Override
    protected String getDatabaseName() {
        return "cosmicworks";
    }
    // </get_database>
}

获取容器

使用 Container 方法修饰器来配置类以表示容器中的项。 创建类以包含要序列化为 JSON 的所有成员。 在此示例中,该类型具有唯一标识符以及用于类别、名称、数量、价格和清除的字段。

package com.microsoft.learn.azure.cosmosdb.nosql.quickstart;

import com.azure.spring.data.cosmos.core.mapping.Container;
import com.azure.spring.data.cosmos.core.mapping.PartitionKey;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

// <get_container>
@Container(containerName = "products", autoCreateContainer = false)
public class Item {
    private String id;
    private String name;
    private Integer quantity;
    private Boolean sale;

    @PartitionKey
    private String category;
    // </get_container>

    public Item() {        
    }

    public Item(String id, String category, String name, Integer quantity, Boolean sale) {
        this.id = id;
        this.category = category;
        this.name = name;
        this.quantity = quantity;
        this.sale = sale;
    }

    public String getId() {
        return this.id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getCategory() {
        return this.category;
    }

    public void setCategory(String category) {
        this.category = category;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getQuantity() {
        return this.quantity;
    }

    public void setQuantity(Integer quantity) {
        this.quantity = quantity;
    }

    public Boolean getSale() {
        return this.sale;
    }

    public void setSale(Boolean sale) {
        this.sale = sale;
    }

    @Override
    public String toString() {
        try {
            ObjectMapper mapper = new ObjectMapper();
            String json = mapper.writeValueAsString(this);
            return json;
        }
        catch (JsonProcessingException e) {
            return String.format("[%s]\tItem:\t%s", this.id, this.name);
        }
    }
}

创建项

使用 repository.save 在容器中创建某个项。

package com.microsoft.learn.azure.cosmosdb.nosql.quickstart;

import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.stereotype.Controller;

import com.azure.cosmos.models.PartitionKey;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;

@Controller
public class StartController {
    
    private static SimpMessageSendingOperations messageSendingOperations;

    private ItemRepository repository;

    @Autowired
    public StartController(SimpMessageSendingOperations messageSendingOperations, ItemRepository repository) {
        StartController.messageSendingOperations = messageSendingOperations;
        this.repository = repository;
    }

    @MessageMapping("/start")
    public void start() throws Exception {
        this.sendMessage("Current Status:\tStarting...");

        repository.deleteAll();

        this.sendMessage(String.format("Get database:\t%s", "cosmicworks"));

        this.sendMessage(String.format("Get container:\t%s", "products"));

        {
            // <create_item>
            Item item = new Item(
                "70b63682-b93a-4c77-aad2-65501347265f",
                "gear-surf-surfboards",
                "Yamba Surfboard",
                12,
                false
            );
            Item created_item = repository.save(item);
            // </create_item>
            this.sendMessage(String.format("Upserted item:\t%s", created_item));
        }

        {
            Item item = new Item(
                "25a68543-b90c-439d-8332-7ef41e06a0e0",
                "gear-surf-surfboards",
                "Kiama Classic Surfboard",
                4,
                true
            );
            Item created_item = repository.save(item);
            this.sendMessage(String.format("Upserted item:\t%s", created_item));
        }

        // <read_item>
        PartitionKey partitionKey = new PartitionKey("gear-surf-surfboards");
        Optional<Item> existing_item = repository.findById("70b63682-b93a-4c77-aad2-65501347265f", partitionKey);
        if (existing_item.isPresent()) {
            // Do something  
        }
        // </read_item>
        existing_item.ifPresent(i -> {
            this.sendMessage(String.format("Read item id:\t%s", i.getId()));
            this.sendMessage(String.format("Read item:\t%s", i));
        });

        // <query_items>
        List<Item> items = repository.getItemsByCategory("gear-surf-surfboards");
        for (Item item : items) {
            // Do something
        }
        // </query_items>
        this.sendMessage("Found items:");
        try {
            ObjectWriter writer = new ObjectMapper().writer().withDefaultPrettyPrinter();
            String json = writer.writeValueAsString(items);
            this.sendMessage(json);
        }
        catch (JsonProcessingException e) {
            this.sendMessage(String.format("Number of items:\t%s", items.size()));
        }
    }

    private void sendMessage(String message)
    {
        StartController.messageSendingOperations.convertAndSend("/topic/output", new Payload(message));
    }
}

读取项

同时使用唯一标识符 (id) 和分区键字段来执行点读取操作。 使用 repository.findById 以有效检索特定项。

package com.microsoft.learn.azure.cosmosdb.nosql.quickstart;

import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.stereotype.Controller;

import com.azure.cosmos.models.PartitionKey;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;

@Controller
public class StartController {
    
    private static SimpMessageSendingOperations messageSendingOperations;

    private ItemRepository repository;

    @Autowired
    public StartController(SimpMessageSendingOperations messageSendingOperations, ItemRepository repository) {
        StartController.messageSendingOperations = messageSendingOperations;
        this.repository = repository;
    }

    @MessageMapping("/start")
    public void start() throws Exception {
        this.sendMessage("Current Status:\tStarting...");

        repository.deleteAll();

        this.sendMessage(String.format("Get database:\t%s", "cosmicworks"));

        this.sendMessage(String.format("Get container:\t%s", "products"));

        {
            // <create_item>
            Item item = new Item(
                "70b63682-b93a-4c77-aad2-65501347265f",
                "gear-surf-surfboards",
                "Yamba Surfboard",
                12,
                false
            );
            Item created_item = repository.save(item);
            // </create_item>
            this.sendMessage(String.format("Upserted item:\t%s", created_item));
        }

        {
            Item item = new Item(
                "25a68543-b90c-439d-8332-7ef41e06a0e0",
                "gear-surf-surfboards",
                "Kiama Classic Surfboard",
                4,
                true
            );
            Item created_item = repository.save(item);
            this.sendMessage(String.format("Upserted item:\t%s", created_item));
        }

        // <read_item>
        PartitionKey partitionKey = new PartitionKey("gear-surf-surfboards");
        Optional<Item> existing_item = repository.findById("70b63682-b93a-4c77-aad2-65501347265f", partitionKey);
        if (existing_item.isPresent()) {
            // Do something  
        }
        // </read_item>
        existing_item.ifPresent(i -> {
            this.sendMessage(String.format("Read item id:\t%s", i.getId()));
            this.sendMessage(String.format("Read item:\t%s", i));
        });

        // <query_items>
        List<Item> items = repository.getItemsByCategory("gear-surf-surfboards");
        for (Item item : items) {
            // Do something
        }
        // </query_items>
        this.sendMessage("Found items:");
        try {
            ObjectWriter writer = new ObjectMapper().writer().withDefaultPrettyPrinter();
            String json = writer.writeValueAsString(items);
            this.sendMessage(json);
        }
        catch (JsonProcessingException e) {
            this.sendMessage(String.format("Number of items:\t%s", items.size()));
        }
    }

    private void sendMessage(String message)
    {
        StartController.messageSendingOperations.convertAndSend("/topic/output", new Payload(message));
    }
}

查询项

通过在存储库的接口中定义一个查询,对容器中的多个项执行查询。 此示例使用 Query 方法修饰器来定义执行此参数化查询的方法:

SELECT * FROM products p WHERE p.category = @category
package com.microsoft.learn.azure.cosmosdb.nosql.quickstart;

import java.util.List;

import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import com.azure.spring.data.cosmos.repository.CosmosRepository;
import com.azure.spring.data.cosmos.repository.Query;

// <repository_implementation>
@Repository
public interface ItemRepository extends CosmosRepository<Item, String> {
    @Query("SELECT * FROM products p WHERE p.category = @category")
    List<Item> getItemsByCategory(@Param("category") String category);
}
// </repository_implementation>

通过使用 repository.getItemsByCategory 提取查询的所有结果。 循环访问查询的结果。

package com.microsoft.learn.azure.cosmosdb.nosql.quickstart;

import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.stereotype.Controller;

import com.azure.cosmos.models.PartitionKey;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;

@Controller
public class StartController {
    
    private static SimpMessageSendingOperations messageSendingOperations;

    private ItemRepository repository;

    @Autowired
    public StartController(SimpMessageSendingOperations messageSendingOperations, ItemRepository repository) {
        StartController.messageSendingOperations = messageSendingOperations;
        this.repository = repository;
    }

    @MessageMapping("/start")
    public void start() throws Exception {
        this.sendMessage("Current Status:\tStarting...");

        repository.deleteAll();

        this.sendMessage(String.format("Get database:\t%s", "cosmicworks"));

        this.sendMessage(String.format("Get container:\t%s", "products"));

        {
            // <create_item>
            Item item = new Item(
                "70b63682-b93a-4c77-aad2-65501347265f",
                "gear-surf-surfboards",
                "Yamba Surfboard",
                12,
                false
            );
            Item created_item = repository.save(item);
            // </create_item>
            this.sendMessage(String.format("Upserted item:\t%s", created_item));
        }

        {
            Item item = new Item(
                "25a68543-b90c-439d-8332-7ef41e06a0e0",
                "gear-surf-surfboards",
                "Kiama Classic Surfboard",
                4,
                true
            );
            Item created_item = repository.save(item);
            this.sendMessage(String.format("Upserted item:\t%s", created_item));
        }

        // <read_item>
        PartitionKey partitionKey = new PartitionKey("gear-surf-surfboards");
        Optional<Item> existing_item = repository.findById("70b63682-b93a-4c77-aad2-65501347265f", partitionKey);
        if (existing_item.isPresent()) {
            // Do something  
        }
        // </read_item>
        existing_item.ifPresent(i -> {
            this.sendMessage(String.format("Read item id:\t%s", i.getId()));
            this.sendMessage(String.format("Read item:\t%s", i));
        });

        // <query_items>
        List<Item> items = repository.getItemsByCategory("gear-surf-surfboards");
        for (Item item : items) {
            // Do something
        }
        // </query_items>
        this.sendMessage("Found items:");
        try {
            ObjectWriter writer = new ObjectMapper().writer().withDefaultPrettyPrinter();
            String json = writer.writeValueAsString(items);
            this.sendMessage(json);
        }
        catch (JsonProcessingException e) {
            this.sendMessage(String.format("Number of items:\t%s", items.size()));
        }
    }

    private void sendMessage(String message)
    {
        StartController.messageSendingOperations.convertAndSend("/topic/output", new Payload(message));
    }
}

下一步