Migración de la aplicación para usar el SDK de Azure Cosmos DB para Java v4

SE APLICA A: NoSQL

Importante

Para más información acerca de este SKD, consulte las notas de la versión del SDK de Azure Cosmos DB para Java v4, el repositorio de Maven, las sugerencias de rendimiento del SDK de Azure Cosmos DB para Java v4 y la guía de solución de problemas del SDK de Azure Cosmos DB para Java v4.

Importante

Como la versión 4 del SDK de Java de Azure Cosmos DB tiene un rendimiento mejorado de hasta un 20 %, el modo directo basado en TCP, así como compatibilidad con las características más recientes del servicio back-end, se recomienda actualizar a la versión 4 en la siguiente oportunidad. Siga leyendo a continuación para obtener más información.

Actualice al SDK de Java de Azure Cosmos DB más reciente para obtener lo mejor que ofrece Azure Cosmos DB: un servicio de base de datos no relacional administrado con rendimiento competitivo, disponibilidad de "cinco 9", gobernanza de recursos únicos, etc. Este artículo explica cómo actualizar la aplicación de Java existente mediante el uso del SDK de Java más antiguo de Azure Cosmos DB a la versión más reciente del SDK de Java 4.0 de Azure Cosmos DB para la API para NoSQL. SDK de Azure Cosmos DB para Java v4 se corresponde con el paquete com.azure.cosmos. Puede usar las instrucciones de este documento si va a migrar la aplicación desde cualquiera de los siguientes SDK de Azure Cosmos DB para Java:

  • SDK de Java 2.x.x sincrónico
  • SDK de Java 2.x.x asincrónico
  • SDK de Java 3.x.x

SDK de Azure Cosmos DB para Java y de las asignaciones de paquetes

En la tabla siguiente se enumeran diferentes SDK de Azure Cosmos DB para Java, el nombre del paquete y la información de la versión:

SDK de Java Fecha de la versión API agrupadas Jar de Maven Nombre del paquete de Java Referencia de API Notas de la versión Fecha de retirada
Asincrónico 2.x.x Junio de 2018 Asincrónico (RxJava) com.microsoft.azure::azure-cosmosdb com.microsoft.azure.cosmosdb.rx API Notas de la versión 31 de agosto de 2024
Sincrónico 2.x.x Septiembre de 2018 Sync com.microsoft.azure::azure-documentdb com.microsoft.azure.cosmosdb API 29 de febrero de 2024
3.x.x Julio de 2019 Asincrónico (reactor)/Sincrónico com.microsoft.azure::azure-cosmos com.azure.data.cosmos API - 31 de agosto de 2024
4.0 Junio de 2020 Asincrónico (reactor)/Sincrónico com.azure::azure-cosmos com.azure.cosmos API - -

Cambios de implementación de nivel de SDK

A continuación se enumeran las diferencias de implementación clave entre los distintos SDK:

RxJava se reemplaza por reactor en el SDK de Azure Cosmos DB para Java versiones 3.x. x y 4.0

Si no está familiarizado con la programación asincrónica o la programación reactiva, consulte la Guía de patrones reactores para ver una introducción a la programación asincrónica y el reactor del proyecto. Esta guía puede ser útil si ha estado usando el SDK de Azure Cosmos DB para Java sincrónico 2.x.x o la API del SDK de Azure Cosmos DB para Java sincrónico 3.x.x en el pasado.

Si ha usado el SDK de Azure Cosmos DB para Java asincrónico 2.x.x y tiene previsto migrar al SDK de 4.0, consulte la guía Reactor vs RxJava para obtener instrucciones sobre cómo convertir el código RxJava para usar el reactor.

El SDK de Azure Cosmos DB para Java v4 tiene el modo de conectividad directa en las API asincrónica y de sincronización

Si ha usado el SDK de Azure Cosmos DB para Java sincrónico 2.x.x, tenga en cuenta que el modo de conexión directa basado en TCP (en lugar de HTTP) se implementa en el SDK de Azure Cosmos DB para Java 4.0 para las API asincrónica y de sincronización.

Cambios en el nivel de API

A continuación se muestran los cambios de nivel de API en el SDK de Azure Cosmos DB para Java 4.x.x en comparación con los SDK anteriores (SDK de Java 3.x.x, SDK de Java asincrónico 2.x.x y SDK de Java sincrónico 2.x.x):

Convenciones de nomenclatura del SDK de Azure Cosmos DB para Java

  • El SDK de Azure Cosmos DB para Java 3.x.x y 4.0 hacen referencia a los recursos de cliente como Cosmos<resourceName>. Por ejemplo: CosmosClient, CosmosDatabase y CosmosContainer. Mientras que en la versión 2.x.x, los SDK de Azure Cosmos DB para Java no tienen un esquema de nomenclatura uniforme.

  • El SDK de Azure Cosmos DB para Java 3.x.x y 4.0 ofrecen API de sincronización y asincrónica.

    • SDK de Java 4.0: Todas las clases pertenecen a la API de sincronización a menos que el nombre de clase se anexe con Async después de Cosmos.

    • SDK de Java 3.x.x: Todas las clases pertenecen a la API asincrónica a menos que el nombre de clase se anexe con Async después de Cosmos.

    • SDK de Java 2.x.x asincrónico: Los nombres de clase son similares a SDK de Java 2.x.x sincrónico, aunque el nombre comienza con Asincrónico.

Estructura jerárquica de la API

El SDK de Azure Cosmos DB para Java 4,.0 y 3.x.x introducen una estructura jerárquica de API que organiza los clientes, las bases de datos y los contenedores de manera anidada, tal y como se muestra en el siguiente fragmento de código del SDK de 4.0:

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

En la versión 2.x.x de Java del SDK de Azure Cosmos DB, todas las operaciones en recursos y documentos se realizan a través de la instancia de cliente.

Representar documentos

En el SDK de Azure Cosmos DB para Java 4.0, los POJO y JsonNodes personalizados son las dos opciones para leer y escribir los documentos desde Azure Cosmos DB.

En el SDK de Azure Cosmos DB para Java 3.x.x, el objeto CosmosItemProperties se expone mediante la API pública y se sirve como una representación del documento. Esta clase ya no se expone públicamente en la versión 4.0.

Importaciones

  • Los paquetes de SDK de Azure Cosmos DB para Java 4.0 comienzan por com.azure.cosmos

  • Los paquetes de SDK de Azure Cosmos DB para Java 3.x.x comienzan por com.azure.data.cosmos

  • Los paquetes de API sincrónica del SDK de Azure Cosmos DB para Java 2.x.x comienzan por com.microsoft.azure.documentdb

  • SDK de Azure Cosmos DB para Java 4.0 coloca varias clases en un com.azure.cosmos.modelsde paquetes anidados. Algunos de estos paquetes incluyen:

    • CosmosContainerResponse
    • CosmosDatabaseResponse
    • CosmosItemResponse
    • Los análogos de la API asincrónica para todos los paquetes anteriores
    • CosmosContainerProperties
    • FeedOptions
    • PartitionKey
    • IndexingPolicy
    • IndexingMode... etc.

Descriptores de acceso

El SDK de Azure Cosmos DB para Java 4.0 expone los métodos get y set para tener acceso a los miembros de instancia. Por ejemplo, la instancia CosmosContainer tiene métodos container.getId() y container.setId().

Esto es diferente del SDK de Azure Cosmos DB para Java 3.x.x, que expone una interfaz fluida. Por ejemplo, una instancia CosmosSyncContainer tiene container.id() que está sobrecargada para obtener o establecer el valor id.

Administración de conflictos de dependencias

La actualización del SDK de Java V2 a V4 de Azure Cosmos DB puede introducir conflictos de dependencia debido a cambios en las bibliotecas usadas por el SDK. La resolución de estos conflictos requiere una administración cuidadosa de las dependencias.

  1. Comprender las nuevas dependencias: El SDK de Azure Cosmos DB V4 tiene su propio conjunto de dependencias que podrían ser diferentes de las de versiones anteriores. Asegúrese de que conoce estas dependencias:

    • 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. Quitar dependencias en conflicto: Empiece por quitar las dependencias relacionadas con versiones anteriores del SDK del archivo pom.xml. Estos incluyen azure-cosmosdb y las dependencias transitivas que podría haber tenido el SDK anterior.

  3. Agregar dependencias del SDK V4: Agregue el SDK V4 y sus dependencias a la pom.xml. Este es un ejemplo:

    <dependency>
        <groupId>com.azure</groupId>
        <artifactId>azure-cosmos</artifactId>
        <version>4.x.x</version> <!-- Use the latest version available -->
    </dependency>
    
  4. Buscar conflictos de dependencia: Use el comando Maven dependency:tree para generar un árbol de dependencias e identificar los conflictos. Ejecute:

    mvn dependency:tree
    

    Busque las versiones en conflicto de las dependencias. Estos conflictos a menudo se producen con bibliotecas como reactor-core, netty-handler, guava, y jackson.

  5. Use Dependency Management: Si se producen conflictos de versiones, es posible que tenga que invalidar las versiones problemáticas mediante la <dependencyManagement>sección de la pom.xml. Este es un ejemplo para aplicar una versión específica 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. Excluir dependencias transitivas: A veces, es posible que tenga que excluir las dependencias transitivas que han traído otras dependencias. Por ejemplo, si otra biblioteca incluye una versión anterior de una dependencia que entra en conflicto, puede excluirla de la siguiente manera:

    <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. Recompilar y probar: Después de realizar estos cambios, recompile el proyecto y pruébelo exhaustivamente para asegurarse de que las nuevas dependencias funcionan correctamente y que no se producen conflictos en tiempo de ejecución.

Comparaciones de fragmentos de código

Crear recursos

En el fragmento de código siguiente se muestran las diferencias en el modo de crear los recursos entre las API 4.0, 3.x.x (asincrónica), 2.x.x (sincrónica) y 2.x.x (asincrónica):


// 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();

Operaciones de elemento

En el fragmento de código siguiente se muestran las diferencias en el modo de realizar las operaciones de elementos entre las API 4.0, 3.x.x (asincrónica), 2.x.x (sincrónica) y 2.x.x (asincrónica):


// 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.

Indización

En el fragmento de código siguiente se muestran las diferencias en el modo de crear la indexación entre las API 4.0, 3.x.x (asincrónica), 2.x.x (sincrónica) y 2.x.x (asincrónica):


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);

Procedimientos almacenados

En el fragmento de código siguiente se muestran las diferencias en el modo de crear procedimientos almacenados entre las API 4.0, 3.x.x (asincrónica), 2.x.x (sincrónica) y 2.x.x (asincrónica):


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();

Fuente de cambios

En el fragmento de código siguiente se muestran las diferencias en el modo en que se ejecutan las operaciones de fuente de cambios entre las API asincrónicas 4.0 y 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();

Período de vida (TTL) de nivel de contenedor

En el siguiente fragmento de código se muestran las diferencias en la forma de crear el período de vida de los datos en el contenedor entre las API 4.0, 3.x.x (asincrónica), 2.x.x (sincrónica) y 2.x.x (asincrónica):


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");

Período de vida (TTL) de nivel de elemento

En el siguiente fragmento de código se muestran las diferencias en la forma de crear el período de vida para un elemento entre las API 4.0, 3.x.x (asincrónica), 2.x.x (sincrónica) y 2.x.x (asincrónica):


// 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
);

Pasos siguientes