Migre seu aplicativo para usar o SDK do Java do Azure Cosmos DB v4

APLICA-SE A: NoSQL

Importante

Para obter mais informações sobre este SDK, veja as Notas de versão do SDK do Java do Azure Cosmos DB v4, repositório do Maven, dicas de desempenho do SDK do Java do Azure Cosmos DB v4 e guia de solução de problemas do SDK do Java do Azure Cosmos DB v4.

Importante

Como o SDK do Azure Cosmos DB Java v4 tem até 20% de taxa de transferência aprimorada, modo direto baseado em TCP e suporte para os recursos de serviço de back-end mais recentes, recomendamos que você atualize para a v4 na próxima oportunidade. Continue lendo para saber mais.

Atualize para o SDK do Java Azure Cosmos DB mais recente para obter o melhor do que o Azure Cosmos DB tem a oferecer: um serviço de banco de dados não relacional gerenciado com desempenho competitivo, disponibilidade de cinco noves, governança de recursos unidirecional e muito mais. Este artigo explica como atualizar o aplicativo Java existente que está usando um SDK do Java do Azure Cosmos DB mais antigo para o SDK do Java do Azure Cosmos DB 4.0 mais recente para API do NoSQL. O SDK do Java do Azure Cosmos DB v4 corresponde ao pacote com.azure.cosmos. Você pode usar as instruções neste documento, se estiver migrando o aplicativo de qualquer um dos seguintes SDKs do Java do Azure Cosmos DB:

  • SDK do Java Síncrono 2.x.x
  • SDK do Java Assíncrono 2.x.x
  • SDK do Java 3.x.x

SDKs do Java e mapeamentos de pacote do Azure Cosmos DB

A tabela a seguir lista diferentes SDKs do Java do Azure Cosmos DB, o nome do pacote e as informações de versão:

Java SDK Data de lançamento Aplicativos agrupados Jar do Maven Nome do pacote do Java Referência de API Notas de versão Data da desativação
Async 2.x.x Junho de 2018 Async(RxJava) com.microsoft.azure::azure-cosmosdb com.microsoft.azure.cosmosdb.rx API Notas de Versão 31 de agosto de 2024
Sync 2.x.x Setembro de 2018 Sincronizar com.microsoft.azure::azure-documentdb com.microsoft.azure.cosmosdb API 29 de fevereiro de 2024
3.x.x Julho de 2019 Async(Reactor)/Sync com.microsoft.azure::azure-cosmos com.azure.data.cosmos API - 31 de agosto de 2024
4,0 Junho de 2020 Async(Reactor)/Sync com.azure::azure-cosmos com.azure.cosmos API - -

Alterações de implementação no nível do SDK

Veja a seguir as principais diferenças de implementação entre SDKs diferentes:

RxJava é substituído pelo reator no SDK do Java do Azure Cosmos DB versões 3.x.x e 4.0

Se você não estiver familiarizado com a programação assíncrona ou programação reativa, confira o guia de padrão do Reator para obter uma introdução à programação assíncrona e ao Reator do Projeto. Este guia pode ser útil se você usou a API Síncrona do SDK do Java Síncrono do Azure Cosmos DB 2.x.x ou do SDK do Java do Azure Cosmos DB 3.x.x no passado.

Se você usou o SDK do Java Assíncrono do Azure Cosmos DB 2.x.x e planeja migrar para o SDK 4.0, confira o Guia de Reator versus RxJava para obter orientação sobre a conversão do código RxJava para usar o Reator.

O SDK do Java do Azure Cosmos DB v4 tem o modo de conectividade direta nas APIs Assíncrona e Síncrona

Se você usou o SDK do Java do Azure Cosmos DB 2.x.x, observe que o modo de conexão direta baseado em TCP (ao contrário do HTTP) foi implementado no SDK do Java do Azure Cosmos DB 4.0 para as APIs Assíncrona e Síncrona.

Alterações no nível da API

Veja a seguir as alterações no nível da API no SDK do Java do Azure Cosmos DB 4.x.x, em comparação aos SDKs anteriores (SDK do Java 3.x.x, SDK do Java Assíncrono 2.x.x e SDK do Java Síncrono 2.x.x):

Convenções de nomenclatura do SDK do Java do Azure Cosmos DB

  • O SDK do Java do Azure Cosmos DB 3.x.x e 4.0 se referem aos recursos do cliente como Cosmos<resourceName>. Por exemplo, CosmosClient, CosmosDatabase, CosmosContainer. Enquanto que na versão 2.x.x, os SDKs do Java do Azure Cosmos DB não têm um esquema de nomenclatura uniforme.

  • Os SDKs do Java do Azure Cosmos DB 3.x.x e 4.0 oferecem APIs Síncrona e Assíncrona.

    • SDK do Java 4.0: Todas as classes pertencem à API Síncrona, a menos que o nome da classe seja anexado com Async depois de Cosmos.

    • SDK do Java 3.x.x: Todas as classes pertencem à API Assíncrona, a menos que o nome da classe seja anexado com Async depois de Cosmos.

    • SDK do Java Assíncrono 2.x.x: Os nomes de classe são semelhantes aos do SDK do Java Síncrono 2.x.x, no entanto, o nome começa com Assíncrono.

Estrutura hierárquica da API

Os SDKs do Java do Azure Cosmos DB 4.0 e 3.x.x introduzem uma estrutura hierárquica da API que organiza os clientes, bancos de dados e contêineres de maneira aninhada, conforme mostrado no seguinte trecho de código do SDK 4.0:

CosmosContainer container = client.getDatabase("MyDatabaseName").getContainer("MyContainerName");

Na versão 2.x.x do SDK do Java do Azure Cosmos DB, todas as operações em recursos e documentos são realizadas por meio da instância do cliente.

Documentos representantes

No SDK do Java do Azure Cosmos DB 4.0, os POJOs personalizados e JsonNodes são as duas opções para ler e gravar os documentos no Azure Cosmos DB.

No SDK do Java do Azure Cosmos DB 3.x.x, o objeto CosmosItemProperties é exposto pela API pública e distribuído como representação do documento. Essa classe não é mais exposta publicamente na versão 4.0.

Importações

  • Os pacotes do SDK do Java do Azure Cosmos DB 4.0 começam com com.azure.cosmos

  • Os pacotes do SDK do Java do Azure Cosmos DB 3.x.x começam com com.azure.data.cosmos

  • Os pacotes da API Assíncrona do SDK do Java do Azure Cosmos DB 2.x.x começam com com.microsoft.azure.documentdb

  • O SDK do Java do Azure Cosmos DB 4.0 coloca várias classes em um pacote aninhado com.azure.cosmos.models. Alguns desses pacotes incluem:

    • CosmosContainerResponse
    • CosmosDatabaseResponse
    • CosmosItemResponse
    • Os análogos da API assíncrona para todos os pacotes acima
    • CosmosContainerProperties
    • FeedOptions
    • PartitionKey
    • IndexingPolicy
    • IndexingMode ...etc.

Acessadores

O SDK do Java do Azure Cosmos DB 4.0 expõe os métodos get e set para acessar os membros da instância. Por exemplo, a instância de CosmosContainer tem os métodos container.getId() e container.setId().

Isso é diferente do SDK do Java do Azure Cosmos DB 3.x.x, que expõe uma interface fluente. Por exemplo, uma instância CosmosSyncContainer tem container.id(), que está sobrecarregado para obter ou definir o valor de id.

Gerenciamento de conflitos de dependência

A atualização do SDK do Java V2 do Azure Cosmos DB para V4 pode introduzir conflitos de dependência devido a alterações nas bibliotecas usadas pelo SDK. Resolver esses conflitos requer um gerenciamento cuidadoso das dependências.

  1. Entenda as novas dependências: o SDK do Azure Cosmos DB V4 tem seu próprio conjunto de dependências que podem ser diferentes daquelas em versões anteriores. Verifique se você está ciente dessas dependências:

    • azure-cosmos
    • reactor-core
    • reactor-netty
    • netty-handler
    • guava
    • slf4j-api
    • jackson-databind
    • jackson-annotations
    • jackson-core
    • commons-lang3
    • commons-collections4
    • azure-core
    • azure-core-http-netty
  2. Remover dependências conflitantes: comece removendo as dependências relacionadas a versões anteriores do SDK do arquivopom.xml. Isso inclui azure-cosmosdb e quaisquer dependências transitivas que o SDK antigo possa ter tido.

  3. Adicionar dependências do SDK V4: adicione o SDK V4 e suas dependências ao pom.xml. Veja um exemplo:

    <dependency>
        <groupId>com.azure</groupId>
        <artifactId>azure-cosmos</artifactId>
        <version>4.x.x</version> <!-- Use the latest version available -->
    </dependency>
    
  4. Verificar se há conflitos de dependência: use o comando dependency:tree Maven para gerar uma árvore de dependência e identificar quaisquer conflitos. Correr:

    mvn dependency:tree
    

    Procure versões conflitantes de dependências. Esses conflitos geralmente ocorrem com bibliotecas como reactor-core, netty-handler, guavae jackson.

  5. Usar o Gerenciamento de Dependências: se você encontrar conflitos de versão, talvez seja necessário substituir versões problemáticas usando a seção <dependencyManagement> em pom.xml. Aqui está um exemplo para impor uma versão específica do reactor-core:

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>io.projectreactor</groupId>
                <artifactId>reactor-core</artifactId>
                <version>3.x.x</version> <!-- Use a compatible version -->
            </dependency>
            <!-- Repeat for any other conflicting dependencies -->
        </dependencies>
    </dependencyManagement>
    
  6. Excluir Dependências Transitivas: às vezes, pode ser necessário excluir dependências transitivas trazidas por outras dependências. Por exemplo, se outra biblioteca trouxer uma versão mais antiga de uma dependência em conflito, você poderá excluí-la assim:

    <dependency>
        <groupId>some.group</groupId>
        <artifactId>some-artifact</artifactId>
        <version>x.x.x</version>
        <exclusions>
            <exclusion>
                <groupId>conflicting.group</groupId>
                <artifactId>conflicting-artifact</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    
  7. Recompilação e teste: depois de fazer essas alterações, recompile o projeto e teste-o minuciosamente para garantir que as novas dependências funcionem corretamente e que não ocorram conflitos de runtime.

Comparações de trecho de código

Criar recursos

O trecho de código a seguir mostra as diferenças em como os recursos são criados entre as APIs 4.0, Assíncrona 3.x.x, Síncrona 2.x.x e Assíncrona 2.x.x:


// Create Async client.
// Building an async client is still a sync operation.
CosmosAsyncClient client = new CosmosClientBuilder()
        .endpoint("your.hostname")
        .key("yourmasterkey")
        .consistencyLevel(ConsistencyLevel.EVENTUAL)
        .buildAsyncClient();

// Create database with specified name
client.createDatabaseIfNotExists("YourDatabaseName")
        .flatMap(databaseResponse -> {
            testDatabaseAsync = client.getDatabase("YourDatabaseName");
            // Container properties - name and partition key
            CosmosContainerProperties containerProperties =
                    new CosmosContainerProperties("YourContainerName", "/id");

            // Provision manual throughput
            ThroughputProperties throughputProperties = ThroughputProperties.createManualThroughput(400);

            // Create container
            return database.createContainerIfNotExists(containerProperties, throughputProperties);
        }).flatMap(containerResponse -> {
    testContainerAsync = database.getContainer("YourContainerName");
    return Mono.empty();
}).subscribe();

Operações de item

O trecho de código a seguir mostra as diferenças em como as operações de item são realizadas entre as APIs 4.0, Assíncrona 3.x.x, Síncrona 2.x.x e Assíncrona 2.x.x:


// Container is created. Generate many docs to insert.
int number_of_docs = 50000;
ArrayList<JsonNode> docs = generateManyDocs(number_of_docs);

// Insert many docs into container...
Flux.fromIterable(docs)
        .flatMap(doc -> testContainerAsync.createItem(doc))
        .subscribe(); // ...Subscribing triggers stream execution.

Indexação

O trecho de código a seguir mostra as diferenças em como a indexação é criada entre as APIs 4.0, Assíncrona 3.x.x, Síncrona 2.x.x e Assíncrona 2.x.x:


CosmosContainerProperties containerProperties = new CosmosContainerProperties(containerName, "/lastName");

// Custom indexing policy
IndexingPolicy indexingPolicy = new IndexingPolicy();
indexingPolicy.setIndexingMode(IndexingMode.CONSISTENT);

// Included paths
List<IncludedPath> includedPaths = new ArrayList<>();
includedPaths.add(new IncludedPath("/*"));
indexingPolicy.setIncludedPaths(includedPaths);

// Excluded paths
List<ExcludedPath> excludedPaths = new ArrayList<>();
excludedPaths.add(new ExcludedPath("/name/*"));
indexingPolicy.setExcludedPaths(excludedPaths);

containerProperties.setIndexingPolicy(indexingPolicy);

ThroughputProperties throughputProperties = ThroughputProperties.createManualThroughput(400);

database.createContainerIfNotExists(containerProperties, throughputProperties);
CosmosAsyncContainer containerIfNotExists = database.getContainer(containerName);

Procedimentos armazenados

O trecho de código a seguir mostra as diferenças em como os procedimentos armazenados são criados entre as APIs 4.0, Assíncrona 3.x.x, Síncrona 2.x.x e Assíncrona 2.x.x:


logger.info("Creating stored procedure...\n");

String sprocId = "createMyDocument";

String sprocBody = "function createMyDocument() {\n" +
        "var documentToCreate = {\"id\":\"test_doc\"}\n" +
        "var context = getContext();\n" +
        "var collection = context.getCollection();\n" +
        "var accepted = collection.createDocument(collection.getSelfLink(), documentToCreate,\n" +
        "    function (err, documentCreated) {\n" +
        "if (err) throw new Error('Error' + err.message);\n" +
        "context.getResponse().setBody(documentCreated.id)\n" +
        "});\n" +
        "if (!accepted) return;\n" +
        "}";

CosmosStoredProcedureProperties storedProcedureDef = new CosmosStoredProcedureProperties(sprocId, sprocBody);
container.getScripts()
        .createStoredProcedure(storedProcedureDef,
                new CosmosStoredProcedureRequestOptions()).block();

// ...

logger.info(String.format("Executing stored procedure %s...\n\n", sprocId));

CosmosStoredProcedureRequestOptions options = new CosmosStoredProcedureRequestOptions();
options.setPartitionKey(new PartitionKey("test_doc"));

container.getScripts()
        .getStoredProcedure(sprocId)
        .execute(null, options)
        .flatMap(executeResponse -> {
            logger.info(String.format("Stored procedure %s returned %s (HTTP %d), at cost %.3f RU.\n",
                    sprocId,
                    executeResponse.getResponseAsString(),
                    executeResponse.getStatusCode(),
                    executeResponse.getRequestCharge()));
            return Mono.empty();
        }).block();

Feed de alteração

O trecho de código a seguir mostra as diferenças em como as operações do feed de alterações são realizadas entre as APIs Assíncronas 4.0 e 3.x.x:


ChangeFeedProcessor changeFeedProcessorInstance =
        new ChangeFeedProcessorBuilder()
                .hostName(hostName)
                .feedContainer(feedContainer)
                .leaseContainer(leaseContainer)
                .handleChanges((List<JsonNode> docs) -> {
                    logger.info("--->setHandleChanges() START");

                    for (JsonNode document : docs) {
                        try {
                            //Change Feed hands the document to you in the form of a JsonNode
                            //As a developer you have two options for handling the JsonNode document provided to you by Change Feed
                            //One option is to operate on the document in the form of a JsonNode, as shown below. This is great
                            //especially if you do not have a single uniform data model for all documents.
                            logger.info("---->DOCUMENT RECEIVED: " + OBJECT_MAPPER.writerWithDefaultPrettyPrinter()
                                    .writeValueAsString(document));

                            //You can also transform the JsonNode to a POJO having the same structure as the JsonNode,
                            //as shown below. Then you can operate on the POJO.
                            CustomPOJO pojo_doc = OBJECT_MAPPER.treeToValue(document, CustomPOJO.class);
                            logger.info("----=>id: " + pojo_doc.getId());

                        } catch (JsonProcessingException e) {
                            e.printStackTrace();
                        }
                    }
                    logger.info("--->handleChanges() END");

                })
                .buildChangeFeedProcessor();

// ...

changeFeedProcessorInstance.start()
        .subscribeOn(Schedulers.elastic())
        .subscribe();

Vida útil (TTL) no nível de contêiner

O trecho de código a seguir mostra as diferenças na criação de vida útil para dados no contêiner, usando as APIs 4.0, Assíncrona 3.x.x, Síncrona 2.x.x e Assíncrona 2.x.x:


CosmosAsyncContainer container;

// Create a new container with TTL enabled with default expiration value
CosmosContainerProperties containerProperties = new CosmosContainerProperties("myContainer", "/myPartitionKey");
containerProperties.setDefaultTimeToLiveInSeconds(90 * 60 * 60 * 24);
ThroughputProperties throughputProperties = ThroughputProperties.createManualThroughput(400);
database.createContainerIfNotExists(containerProperties, throughputProperties).block();
container = database.getContainer("myContainer");

Vida útil (TTL) no nível de item

O trecho de código a seguir mostra as diferenças na criação de vida útil para um item, entre as APIs 4.0, Assíncrona 3.x.x, Síncrona 2.x.x e Assíncrona 2.x.x:


// Include a property that serializes to "ttl" in JSON
class SalesOrder
{
    private String id;
    private String customerId;
    private Integer ttl;

    public SalesOrder(String id, String customerId, Integer ttl) {
        this.id = id;
        this.customerId = customerId;
        this.ttl = ttl;
    }

    public String getId() {return this.id;}
    public void setId(String new_id) {this.id = new_id;}
    public String getCustomerId() {return this.customerId;}
    public void setCustomerId(String new_cid) {this.customerId = new_cid;}
    public Integer getTtl() {return this.ttl;}
    public void setTtl(Integer new_ttl) {this.ttl = new_ttl;}

    //...
}


// Set the value to the expiration in seconds
SalesOrder salesOrder = new SalesOrder(
        "SO05",
        "CO18009186470",
        60 * 60 * 24 * 30  // Expire sales orders in 30 days
);

Próximas etapas