Partager via


Migration d’une application de façon à utiliser le kit SDK Java Azure Cosmos DB v4

S’APPLIQUE À : NoSQL

Important

Pour plus d’informations sur le kit SDK Java Azure Cosmos DB v4, consultez les Notes de publication, le Référentiel Maven, les Conseils en matière de performances et le Guide de résolution des problèmes correspondants.

Important

Le kit SDK Java v4 Azure Cosmos DB présentant jusqu’à 20% de débit amélioré, le mode direct basé sur TCP et la prise en charge des dernières fonctionnalités du service back-end, nous vous recommandons d’adopter v4 dès que vous en aurez la possibilité. Poursuivez votre lecture pour en savoir plus.

Procédez à une mise à jour vers le kit de développement logiciel (SDK) Java Azure Cosmos DB le plus récent pour tirer le meilleur parti de l’offre Azure Cosmos DB : un service de base de données non relationnelle géré avec des performances compétitives, une disponibilité de 99,999 %, une gouvernance des ressources de type « un-à-plusieurs » et bien plus encore. Cet article explique comment mettre à niveau une application Java existante qui utilise un ancien kit SDK Java Azure Cosmos DB vers la version 4.0, plus récente, pour l’API pour NoSQL. Le kit SDK Java Azure Cosmos DB v4 correspond au package com.azure.cosmos. Vous pouvez suivre les instructions de ce document si vous migrez votre application à partir de l’un des kits SDK Java Azure Cosmos DB suivants :

  • Kit SDK Java Sync 2.x.x
  • Kit SDK Java Async 2.x.x
  • Kit SDK Java 3.x.x

Correspondance entre les kits SDK Java Azure Cosmos DB et les packages

Le tableau suivant présente les différents kits SDK Java Azure Cosmos DB, le nom du package et les informations de version :

Kit de développement logiciel (SDK) Java Date de sortie API groupées JAR Maven Nom du package Java Référence API Notes de publication Date de mise hors service
Async 2.x.x Juin 2018 Async(RxJava) com.microsoft.azure::azure-cosmosdb com.microsoft.azure.cosmosdb.rx API Notes de publication 31 août 2024
Sync 2.x.x Septembre 2018 Synchronisation com.microsoft.azure::azure-documentdb com.microsoft.azure.cosmosdb API 29 février 2024
3.x.x Juillet 2019 Async(Reactor)/Sync com.microsoft.azure::azure-cosmos com.azure.data.cosmos API - 31 août 2024
4.0 Juin 2020 Async(Reactor)/Sync com.azure::azure-cosmos com.azure.cosmos API - -

Modifications apportées à l’implémentation au niveau du kit SDK

Voici les principales différences d’implémentation entre les kits SDK :

RxJava est remplacé par Reactor dans les versions 3.x.x et 4.0 du kit SDK Java Azure Cosmos DB

Si vous ne connaissez pas la programmation asynchrone ni la programmation réactive, consultez Guide du modèle Reactor pour une présentation de la programmation asynchrone et de Project Reactor. Ce guide peut être utile si vous utilisiez le kit SDK Java Azure Cosmos DB Sync 2.x.x ou l’API Sync du kit SDK Java Azure Cosmos DB 3.x.x par le passé.

Si vous utilisiez le kit SDK Java Azure Cosmos DB Async 2.x.x et que vous envisagez de migrer vers la version 4.0 du kit SDK, consultez le Guide comparatif Reactor-RxJava pour obtenir des conseils sur la conversion du code RxJava vers Reactor.

Le kit SDK Java Azure Cosmos DB v4 comporte un mode de connectivité directe dans les API Async et Sync

Si vous utilisiez le kit SDK Java Azure Cosmos DB Sync 2.x.x, sachez que le mode de connexion directe sur TCP (et non HTTP) est implémenté dans le kit SDK Java Azure Cosmos DB 4.0 pour les API Async et Sync.

Modifications apportées au niveau de l’API

Les modifications apportées au niveau de l’API dans le kit SDK Java Azure Cosmos DB 4.x.x par rapport aux anciens kits SDK (3.x.x, Async 2.x.x et Sync 2.x.x) sont les suivantes :

Conventions d’affectation de noms des SDK Java Azure Cosmos DB

  • Les versions 3.x et 4.0 du kit SDK Java Azure Cosmos DB font référence ainsi aux ressources clientes : Cosmos<resourceName>. Par exemple, CosmosClient, CosmosDatabase, CosmosContainer. À l’inverse, dans la version 2.x.x, les kits SDK Java Azure Cosmos DB ne présentent pas de modèle uniforme d’affectation de noms.

  • Les versions 3.x et 4.0 du kit SDK Java Azure Cosmos DB proposent à la fois des API Sync et Async.

    • Java SDK 4.0 : toutes les classes appartiennent à l’API Sync, sauf si le nom de la classe comporte Async après Cosmos.

    • Java SDK 3.x.x : toutes les classes appartiennent à l’API Async, sauf si le nom de la classe comporte Async après Cosmos.

    • Kit SDK Java Async 2.x.x : les noms de classe sont similaires à ceux du kit SDK Java 2.x.x, à ceci près que le nom commence par Async.

Structure d’API hiérarchique

Les versions 4.0 et 3.x.x du kit SDK Java Azure Cosmos DB introduisent une structure d’API hiérarchique qui organise les clients, les bases de données et les conteneurs de manière imbriquée, comme l’illustre l’extrait de code du kit SDK 4.0 suivant :

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

Dans la version 2.x.x du kit SDK Java Azure Cosmos DB, toutes les opérations effectuées sur les ressources et les documents passent par l’instance cliente.

Représentation des documents

Dans la version 4.0 du kit SDK Java Azure Cosmos DB, CustomPOJO et JsonNodes constituent les deux options permettant de lire et d’écrire les documents à partir d’Azure Cosmos DB.

Dans la version 3.x.x du kit SDK Java Azure Cosmos DB, l’objet CosmosItemProperties est exposé par l’API publique et traité comme une représentation de document. Cette classe n’est plus exposée publiquement dans la version 4.0.

Importations

  • Les packages du kit SDK Java Azure Cosmos DB 4.0 commencent par com.azure.cosmos.

  • Les packages du kit SDK Java Azure Cosmos DB 3.x.x commencent par com.azure.data.cosmos.

  • Les packages de l’APO Sync du Kit de développement logiciel (SDK) Java Azure Cosmos DB 2.x.x commencent par com.microsoft.azure.documentdb.

  • Le kit SDK Java Azure Cosmos DB 4.0 place plusieurs classes dans un package imbriqué com.azure.cosmos.models. Exemples de packages :

    • CosmosContainerResponse
    • CosmosDatabaseResponse
    • CosmosItemResponse
    • L’équivalent API Async de tous les packages ci-dessus
    • CosmosContainerProperties
    • FeedOptions
    • PartitionKey
    • IndexingPolicy
    • IndexingMode

Accesseurs

La version 4.0 du kit SDK Java Azure Cosmos DB expose les méthodes get et set pour accéder aux membres de l’instance. Par exemple, l’instance de CosmosContainer comporte les méthodes container.getId() et container.setId().

À l’inverse, la version 3.x.x du kit SDK Java Azure Cosmos DB expose une interface Fluent. Par exemple, une instance de CosmosSyncContainer comporte container.id(), qui est surchargée pour obtenir ou définir la valeur id.

Gestion des conflits de dépendance

La mise à niveau du SDK Java Azure Cosmos DB V2 vers V4 peut introduire des conflits de dépendance en raison des modifications apportées aux bibliothèques utilisées par le SDK. La résolution de ces conflits nécessite une gestion minutieuse des dépendances.

  1. Comprendre les nouvelles dépendances : le SDK Azure Cosmos DB V4 a son propre ensemble de dépendances qui peuvent être différentes de celles des versions antérieures. Assurez-vous de connaître ces dépendances :

    • 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. Supprimer les dépendances en conflit : commencez par supprimer les dépendances associées aux versions antérieures du SDK de votre fichier pom.xml. Celles-ci incluent azure-cosmosdb et toutes les dépendances transitives que l’ancien SDK a peut-être eues.

  3. Ajouter les dépendances du SDK V4 : ajoutez le SDK V4 et ses dépendances à votre pom.xml. Voici un exemple :

    <dependency>
        <groupId>com.azure</groupId>
        <artifactId>azure-cosmos</artifactId>
        <version>4.x.x</version> <!-- Use the latest version available -->
    </dependency>
    
  4. Rechercher les conflits de dépendance : utilisez la commande Maven dependency:tree pour générer une arborescence de dépendances et identifier les conflits. Run :

    mvn dependency:tree
    

    Recherchez les versions en conflit des dépendances. Ces conflits se produisent souvent avec des bibliothèques telles que reactor-core, netty-handler, guava, et jackson.

  5. Utiliser la gestion des dépendances : si vous rencontrez des conflits de version, vous devrez peut-être remplacer les versions problématiques à l’aide de la section <dependencyManagement> de votre pom.xml. Voici un exemple montrant comment appliquer une version spécifique de 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. Exclure les dépendances transitives : parfois, vous devrez peut-être exclure les dépendances transitives introduites par d’autres dépendances. Par exemple, si une autre bibliothèque apporte une version antérieure d’une dépendance qui est en conflit, vous pouvez l’exclure comme suit :

    <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. Regénérer et tester : après avoir apporté ces modifications, régénérez votre projet et testez-le soigneusement pour vous assurer que les nouvelles dépendances fonctionnent correctement et qu’aucun conflit d’exécution ne se produit.

Comparaison d’extraits de code

Créer des ressources

L’extrait de code suivant montre les différences dans la façon dont les ressources sont créées entre les API Async 4.0, 3.x.x, Sync 2.x.x et les API ASync 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();

Opérations d'élément

L’extrait de code suivant montre les différences dans la façon dont les opérations d’élément sont effectuées entre les API Async 4.0, 3.x.x, Sync 2.x.x et les API Async 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.

Indexation

L’extrait de code suivant montre les différences dans la façon dont l’indexation est créée entre les API Async 4.0, 3.x.x, Sync 2.x.x et les API Async 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);

Procédures stockées

L’extrait de code suivant montre les différences dans la façon dont les procédures stockées sont créées entre les API Async 4.0, 3.x.x, Sync 2.x.x et les API Async 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();

Modifier le flux

L’extrait de code suivant montre les différences dans la façon dont les opérations de flux de modification sont exécutées entre les API Async 4.0 et 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();

Durée de vie (TTL) au niveau du conteneur

L’extrait de code suivant montre les différences dans la façon dont la durée de vie des données du conteneur est créée entre les API Async 4.0, 3.x.x, Sync 2.x.x et les API Async 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");

Durée de vie (TTL) au niveau de l’élément

L’extrait de code suivant montre les différences dans la façon dont la durée de vie d’un élément est créée entre les API Async 4.0, 3.x.x, Sync 2.x.x et les API Async 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
);

Étapes suivantes