Biblioteca cliente de Azure Spring Data Cosmos para Java

Azure Spring Data Cosmos proporciona compatibilidad con Spring Data para Azure Cosmos DB mediante la SQL API, basada en la plataforma de Spring Data. Azure Cosmos DB es un servicio de base de datos de distribución global que permite a los desarrolladores trabajar con datos mediante diversas API estándar, como SQL, MongoDB, Cassandra, Graph y Table.

Compatibilidad con versiones de Spring Data

Este proyecto admite las versiones spring-data-commons 2.2.x y spring-data-commons 2.3.x. Los usuarios de Maven pueden heredar del proyecto spring-boot-starter-parent para obtener una sección de administración de dependencias que permita a Spring administrar las versiones para las dependencias.

<!-- Inherit defaults from Spring Boot -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>${spring.boot.version}</version>
</parent>

Con esta configuración, también puede invalidar dependencias individuales al invalidar una propiedad en su propio proyecto. Por ejemplo, para actualizar a otra serie de versiones de Spring Data, agregaría lo siguiente al archivo pom.xml.

<properties>
    <spring-data-releasetrain.version>${spring.data.version}</spring-data-releasetrain.version>
</properties>

Si no desea utilizar spring-boot-starter-parent, todavía puede mantener las ventajas de la administración de dependencias mediante una dependencia scope=import:

<dependencyManagement>
     <dependencies>
        <dependency>
            <!-- Import dependency management from Spring Boot -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring.boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

Esta configuración no permite invalidar dependencias individuales mediante una propiedad, tal como se ha explicado anteriormente. Para lograr el mismo resultado, debe agregar una entrada en dependencyManagement del proyecto antes de la entrada spring-boot-dependencies. Por ejemplo, para actualizar a otra serie de versiones de Spring Data, agregaría lo siguiente a pom.xml.

<dependencyManagement>
    <dependencies>
        <!-- Override Spring Data release train provided by Spring Boot -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-releasetrain</artifactId>
            <version>${spring.data.version}</version>
            <scope>import</scope>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring.boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

Nota

Reemplace ${spring.boot.version} y ${spring.data.version} por las versiones de Spring Boot y Spring Data que quiere usar en el proyecto.

Introducción

Inclusión del paquete

Si utiliza Maven, agregue la siguiente dependencia.

<dependency>
    <groupId>com.azure</groupId>
    <artifactId>azure-spring-data-cosmos</artifactId>
    <version>3.13.1</version>
</dependency>

Prerrequisitos

  • Kit de desarrollo de Java (JDK), versión 8 o posterior.
  • Una cuenta de Azure activa. Si no tiene una, puede registrarse para obtener una cuenta gratuita. Como alternativa, puede usar el Emulador de Azure Cosmos DB para el desarrollo y las pruebas. Como el certificado https del emulador está autofirmado, debe importar su certificado al almacén de certificados de confianza de Java, tal como se explica aquí.
  • (Opcional) SLF4J es una fachada de registro.
  • (Opcional) El enlace SLF4J se utiliza para asociar una plataforma de registro específica a SLF4J.
  • (Opcional) Maven

SLF4J solo es necesario si planea utilizar el registro; descargue también un enlace SLF4J que vinculará SLF4J API con la implementación de registro de su elección. Consulte el manual del usuario de SLF4J para más información.

Configuración de la clase Configuration

Para configurar la clase Configuration, deberá ampliar AbstractCosmosConfiguration.

Azure-spring-data-cosmos también admite Response Diagnostics String y Query Metrics. Establezca la marca queryMetricsEnabled en true en application.properties para habilitar las métricas de consulta. Además de establecer la marca, implemente ResponseDiagnosticsProcessor para registrar la información de diagnóstico.

@Configuration
@EnableCosmosRepositories
public class AppConfiguration extends AbstractCosmosConfiguration {

    private static final Logger LOGGER = LoggerFactory.getLogger(AppConfiguration.class);

    @Value("${azure.cosmos.uri}")
    private String uri;

    @Value("${azure.cosmos.key}")
    private String key;

    @Value("${azure.cosmos.secondaryKey}")
    private String secondaryKey;

    @Value("${azure.cosmos.database}")
    private String dbName;

    @Value("${azure.cosmos.queryMetricsEnabled}")
    private boolean queryMetricsEnabled;

    private AzureKeyCredential azureKeyCredential;

    @Bean
    public CosmosClientBuilder getCosmosClientBuilder() {
        this.azureKeyCredential = new AzureKeyCredential(key);
        DirectConnectionConfig directConnectionConfig = new DirectConnectionConfig();
        GatewayConnectionConfig gatewayConnectionConfig = new GatewayConnectionConfig();
        return new CosmosClientBuilder()
            .endpoint(uri)
            .credential(azureKeyCredential)
            .directMode(directConnectionConfig, gatewayConnectionConfig);
    }

    @Override
    public CosmosConfig cosmosConfig() {
        return CosmosConfig.builder()
                           .enableQueryMetrics(queryMetricsEnabled)
                           .responseDiagnosticsProcessor(new ResponseDiagnosticsProcessorImplementation())
                           .build();
    }

    public void switchToSecondaryKey() {
        this.azureKeyCredential.update(secondaryKey);
    }

    @Override
    protected String getDatabaseName() {
        return "testdb";
    }

    private static class ResponseDiagnosticsProcessorImplementation implements ResponseDiagnosticsProcessor {

        @Override
        public void processResponseDiagnostics(@Nullable ResponseDiagnostics responseDiagnostics) {
            LOGGER.info("Response Diagnostics {}", responseDiagnostics);
        }
    }

}

Personalización de la configuración

Puede personalizar DirectConnectionConfig o GatewayConnectionConfig o ambos y proporcionarlos al bean CosmosClientBuilder para personalizar CosmosAsyncClient.

@Bean
public CosmosClientBuilder getCosmosClientBuilder() {

    DirectConnectionConfig directConnectionConfig = new DirectConnectionConfig();
    GatewayConnectionConfig gatewayConnectionConfig = new GatewayConnectionConfig();
    return new CosmosClientBuilder()
        .endpoint(uri)
        .directMode(directConnectionConfig, gatewayConnectionConfig);
}

@Override
public CosmosConfig cosmosConfig() {
    return CosmosConfig.builder()
                       .enableQueryMetrics(queryMetricsEnabled)
                       .responseDiagnosticsProcessor(new ResponseDiagnosticsProcessorImplementation())
                       .build();
}

De forma predeterminada, @EnableCosmosRepositories examinará el paquete actual en busca de interfaces que extiendan una de las interfaces del repositorio de Spring Data. Utilícelo para anotar la clase Configuration a fin de examinar un paquete raíz diferente mediante @EnableCosmosRepositories(basePackageClass=UserRepository.class) si el diseño del proyecto tiene varios proyectos.

Definición de una entidad

Defina una entidad simple como elemento en Azure Cosmos DB.

Para definir entidades, puede agregar la anotación @Container y especificar las propiedades relacionadas con el contenedor, como el nombre del contenedor, las unidades de solicitud (RU), el período de vida y la marca de creación automática del contenedor.

Los contenedores se crearán automáticamente a menos que no lo quiera. Establezca autoCreateContainer en false en la anotación @Container para deshabilitar la creación automática de contenedores.

Nota

De forma predeterminada, las unidades de solicitud asignadas a los contenedores recién creados son 400. Especifique un valor de RU diferente para personalizar las unidades de solicitud para el contenedor creado por el SDK (el valor mínimo de RU es de 400).

@Container(containerName = "myContainer", ru = "400")
public class User {
    private String id;
    private String firstName;


    @PartitionKey
    private String lastName;

    public User() {
        // If you do not want to create a default constructor,
        // use annotation @JsonCreator and @JsonProperty in the full args constructor
    }

    public User(String id, String firstName, String lastName) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
    }

    @Override
    public String toString() {
        return String.format("User: %s %s, %s", firstName, lastName, id);
    }

    public String getId() {
        return id;
    }

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

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}

El campo id se usará como identificador de elemento en Azure Cosmos DB. Si desea usar otro campo como firstName como id de elemento, solo tiene que anotar ese campo con la anotación @Id.

La anotación @Container(containerName="myContainer") especifica el nombre del contenedor en Azure Cosmos DB.

La anotación @PartitionKey del campo lastName especifica este campo como clave de partición en Azure Cosmos DB.

Creación de contenedores con un rendimiento de escalabilidad automática

La anotación del campo autoScale especifica el contenedor que se va a crear con un rendimiento de escalabilidad automática si se establece en true. El valor predeterminado es false, lo que significa que los contenedores se crean con un rendimiento manual.

Lea más información sobre el rendimiento de escalabilidad automática aquí.

@Container(containerName = "myContainer", autoScale = true, ru = "4000")
public class UserSample {
    @Id
    private String emailAddress;

}

Compatibilidad con la clave de partición anidada

El SDK de Spring Data Cosmos admite la clave de partición anidada. Para agregar la clave de partición anidada, utilice el campo partitionKeyPath de la anotación @Container.

Solo se debe utilizar partitionKeyPath para que admita la ruta de acceso de la clave de partición anidada. Para obtener una compatibilidad general con la clave de partición, utilice la anotación @PartitionKey.

De forma predeterminada, la anotación @PartitionKey tendrá prioridad, a menos que no se especifique.

En el ejemplo siguiente se muestra cómo usar correctamente la característica de clave de partición anidada.

@Container(containerName = "nested-partition-key", partitionKeyPath = "/nestedEntitySample/nestedPartitionKey")
public class NestedPartitionKeyEntitySample {

    private NestedEntitySample nestedEntitySample;
}
public class NestedEntitySample {
    private String nestedPartitionKey;
}

Creación de repositorios

Extiende la interfaz de CosmosRepository, que proporciona compatibilidad con el repositorio de Spring Data.

@Repository
public interface UserRepository extends CosmosRepository<User, String> {
    Iterable<User> findByFirstName(String firstName);
    long countByFirstName(String firstName);
    User findOne(String id, String lastName);
}
  • El método findByFirstName es un método de consulta personalizado que buscará elementos por firstName.

QueryAnnotation: uso de consultas anotadas en repositorios

Azure Spring Data Cosmos admite la especificación de consultas anotadas en los repositorios mediante @Query.

Estos son algunos ejemplos de consultas anotadas en el elemento CosmosRepository sincrónico:

public interface AnnotatedQueriesUserRepositoryCodeSnippet extends CosmosRepository<User, String> {
    @Query("select * from c where c.firstName = @firstName and c.lastName = @lastName")
    List<User> getUsersByFirstNameAndLastName(@Param("firstName") String firstName, @Param("lastName") String lastName);

    @Query("select * from c offset @offset limit @limit")
    List<User> getUsersWithOffsetLimit(@Param("offset") int offset, @Param("limit") int limit);

    @Query("select value count(1) from c where c.firstName = @firstName")
    long getNumberOfUsersWithFirstName(@Param("firstName") String firstName);
}

Estos son algunos ejemplos de consultas anotadas en el elemento ReactiveCosmosRepository.

public interface AnnotatedQueriesUserReactiveRepositoryCodeSnippet extends ReactiveCosmosRepository<User, String> {
    @Query("select * from c where c.firstName = @firstName and c.lastName = @lastName")
    Flux<User> getUsersByTitleAndValue(@Param("firstName") int firstName, @Param("lastName") String lastName);

    @Query("select * from c offset @offset limit @limit")
    Flux<User> getUsersWithOffsetLimit(@Param("offset") int offset, @Param("limit") int limit);

    @Query("select count(c.id) as num_ids, c.lastName from c group by c.lastName")
    Flux<ObjectNode> getCoursesGroupByDepartment();

    @Query("select value count(1) from c where c.lastName = @lastName")
    Mono<Long> getNumberOfUsersWithLastName(@Param("lastName") String lastName);
}

Las consultas especificadas en la anotación son las mismas que las consultas de Cosmos. Consulte los artículos siguientes para más información sobre las consultas SQL en Cosmos:

Creación de una clase Application

Aquí se crea una clase Application con todos los componentes.

@SpringBootApplication
public class SampleApplication implements CommandLineRunner {

    @Autowired
    private UserRepository repository;

    @Autowired
    private ApplicationContext applicationContext;

    public static void main(String[] args) {
        SpringApplication.run(SampleApplication.class, args);
    }

    public void run(String... var1) {

        final User testUser = new User("testId", "testFirstName", "testLastName");

        repository.deleteAll();
        repository.save(testUser);

        // to find by Id, please specify partition key value if collection is partitioned
        final User result = repository.findOne(testUser.getId(), testUser.getLastName());

        //  Switch to secondary key
        UserRepositoryConfiguration bean =
            applicationContext.getBean(UserRepositoryConfiguration.class);
        bean.switchToSecondaryKey();

        //  Now repository will use secondary key
        repository.save(testUser);

    }
}

Conexión automática de la interfaz UserRepository para realizar operaciones como guardar, eliminar, buscar, etc.

Spring Data Azure Cosmos DB usa y para ejecutar las consultas detrás CosmosTemplateReactiveCosmosTemplate de CosmosTemplatey ReactiveCosmosTemplate Puede utilizar la plantilla usted mismo para consultas más complejas.

Conceptos clave

CrudRepository y ReactiveCrudRepository

Azure Spring Data Cosmos admite ReactiveCrudRepository y CrudRepository, que proporciona la funcionalidad CRUD básica:

  • Guardar
  • findAll
  • findOne by ID
  • deleteAll
  • delete by ID
  • delete entity

Anotaciones de Spring Data

Hay dos maneras de asignar un campo de la clase de dominio al campo del elemento de base de datos de Azure Cosmos: - anotar un campo en la clase de dominio con , este campo se asignará id a Item en Cosmos @Idid DB. : establezca el nombre de este campo en , este campo id se asignará al elemento id en Azure Cosmos DB.

Admite la generación automática de UUID de tipo cadena mediante la anotación @GeneratedValue cadena. El campo de identificación de una entidad con un identificador de tipo cadena se puede anotar con @GeneratedValue para generar automáticamente un UUID aleatorio antes de la inserción.

public class GeneratedIdEntity {

    @Id
    @GeneratedValue
    private String id;

}
  • Expresión SpEL y nombre de contenedor personalizado.
    • De forma predeterminada, el nombre del contenedor será el nombre de clase de la clase de dominio de usuario. Para personalizarlo, agregue la anotación @Container(containerName="myCustomContainerName") a la clase de dominio. El campo de contenedor también admite expresiones SpEL (por ejemplo, container = "${dynamic.container.name}" o container = "#{@someBean.getContainerName()}") para proporcionar nombres de contenedor mediante programación o a través de propiedades de configuración.
    • Para que las expresiones SpEL funcionen correctamente, debe agregar @DependsOn("expressionResolver") a partir de la clase Spring Application.
@SpringBootApplication
@DependsOn("expressionResolver")
public class SampleApplication {

}
  • IndexingPolicy personalizado. De forma predeterminada, el servicio de Azure es quien establecerá IndexingPolicy. Para personalizarlo, agregue una anotación @CosmosIndexingPolicy a la clase de dominio. Esta anotación tiene 4 atributos que se pueden personalizar; consulte lo siguiente:
// Indicate if indexing policy use automatic or not
// Default value is true
boolean automatic() default Constants.DEFAULT_INDEXING_POLICY_AUTOMATIC;

// Indexing policy mode, option Consistent.
IndexingMode mode() default IndexingMode.CONSISTENT;

// Included paths for indexing
String[] includePaths() default {};

// Excluded paths for indexing
String[] excludePaths() default {};

Partición de Azure Cosmos DB

Azure-spring-data-cosmos admite la partición de Azure Cosmos DB.

Para especificar que un campo de clase de dominio sea un campo de clave de partición, solo tiene que anotarlo con @PartitionKey.

Cuando realice una operación CRUD, especifique el valor de la partición.

Para obtener más ejemplos de CRUD de partición, consulte la prueba aquí.

Bloqueo optimista

Azure-spring-data-cosmos admite el bloqueo optimista para contenedores específicos, lo que significa que las operaciones upsert/delete por elemento producirán un error con una excepción en caso de que otro proceso modifique el elemento mientras tanto.

Si quiere habilitar el bloqueo optimista para un contenedor, basta con crear un campo _etag de cadena y marcarlo con la anotación @Version. Consulte lo siguiente:

@Container(containerName = "myContainer")
public class MyItem {
    String id;
    String data;
    @Version
    String _etag;
}

Consulta personalizada, paginable y ordenación de Spring Data

Azure-spring-data-cosmos admite consultas personalizadas de spring data,por ejemplo, una operación de búsqueda como

Admite Pageable, Slice y Sort de Spring Data. - En función de las RU disponibles en la cuenta de base de datos, cosmosDB puede devolver elementos menores o iguales que el tamaño solicitado. - Debido a este número variable de elementos devueltos en cada iteración, el usuario no debe confiar en totalPageSize y, en su lugar, la iteración sobre paginable debe realizarse de esta manera.

private List<T> findAllWithPageSize(int pageSize) {

    final CosmosPageRequest pageRequest = new CosmosPageRequest(0, pageSize, null);
    Page<T> page = repository.findAll(pageRequest);
    List<T> pageContent = page.getContent();
    while (page.hasNext()) {
        Pageable nextPageable = page.nextPageable();
        page = repository.findAll(nextPageable);
        pageContent = page.getContent();
    }
    return pageContent;
}
public interface SliceQueriesUserRepository extends CosmosRepository<User, String> {
    @Query("select * from c where c.lastName = @lastName")
    Slice<User> getUsersByLastName(@Param("lastName") String lastName, Pageable pageable);
}
private List<User> getUsersByLastName(String lastName, int pageSize) {

    final CosmosPageRequest pageRequest = new CosmosPageRequest(0, pageSize, null);
    Slice<User> slice = repository.getUsersByLastName(lastName, pageRequest);
    List<User> content = slice.getContent();
    while (slice.hasNext()) {
        Pageable nextPageable = slice.nextPageable();
        slice = repository.getUsersByLastName(lastName, nextPageable);
        content.addAll(slice.getContent());
    }
    return content;
}

Spring Boot Starter Data Rest

Azure-spring-data-cosmos admite spring-boot-starter-data-rest.

  • Admite List y el tipo anidado en la clase de dominio.
  • Bean de ObjectMapper configurable con nombre único cosmosObjectMapper, configure ObjectMapper personalizado solo si realmente lo necesita. Por ejemplo:
@Bean(name = "cosmosObjectMapper")
public ObjectMapper objectMapper() {
    return new ObjectMapper(); // Do configuration to the ObjectMapper if required
}

Auditoría

Azure-spring-data-cosmos admite la auditoría de campos en entidades de bases de datos mediante anotaciones de spring-data estándar.

Para habilitar esta característica, agregue una anotación @EnableCosmosAuditing a la configuración de la aplicación.

Las entidades pueden anotar campos mediante @CreatedBy, @CreatedDate, @LastModifiedBy y @LastModifiedDate. Estos campos se actualizarán automáticamente.

@Container(containerName = "myContainer")
public class AuditableUser {
    private String id;
    private String firstName;
    @CreatedBy
    private String createdBy;
    @CreatedDate
    private OffsetDateTime createdDate;
    @LastModifiedBy
    private String lastModifiedBy;
    @LastModifiedDate
    private OffsetDateTime lastModifiedByDate;
}

Configuración de varias bases de datos

Azure-spring-data-cosmos admite la configuración de varias bases de datos, incluidas "cuentas de varias bases de datos" y "cuenta única, con varias bases de datos".

Cuentas de varias bases de datos

En el ejemplo se usa el archivo application.properties:

# primary account cosmos config
azure.cosmos.primary.uri=your-primary-cosmosDb-uri
azure.cosmos.primary.key=your-primary-cosmosDb-key
azure.cosmos.primary.secondaryKey=your-primary-cosmosDb-secondary-key
azure.cosmos.primary.database=your-primary-cosmosDb-dbName
azure.cosmos.primary.populateQueryMetrics=if-populate-query-metrics

# secondary account cosmos config
azure.cosmos.secondary.uri=your-secondary-cosmosDb-uri
azure.cosmos.secondary.key=your-secondary-cosmosDb-key
azure.cosmos.secondary.secondaryKey=your-secondary-cosmosDb-secondary-key
azure.cosmos.secondary.database=your-secondary-cosmosDb-dbName
azure.cosmos.secondary.populateQueryMetrics=if-populate-query-metrics

La definición de Entity y Repository es similar a la anterior. Puede colocar diferentes entidades de bases de datos en distintos paquetes.

@EnableReactiveCosmosRepositories o @EnableCosmosRepositories admiten la plantilla de Cosmos definida por el usuario, utilice reactiveCosmosTemplateRef o cosmosTemplateRef para configurar el nombre del bean ReactiveCosmosTemplate o CosmosTemplate que se usará con los repositorios detectados.

Si tiene cuentas de Cosmos de varias bases de datos, puede definir varios CosmosAsyncClient. Si la cuenta de Cosmos única tiene varias bases de datos, puede utilizar el mismo CosmosAsyncClient para inicializar la plantilla de Cosmos.

@Configuration
@EnableReactiveCosmosRepositories(basePackages = "com.azure.spring.sample.cosmos.multi.database.multiple.account.repository",
    reactiveCosmosTemplateRef = "primaryDatabaseTemplate")
public class PrimaryDatasourceConfiguration extends AbstractCosmosConfiguration{

    private static final String PRIMARY_DATABASE = "primary_database";

    @Bean
    @ConfigurationProperties(prefix = "azure.cosmos.primary")
    public CosmosProperties primary() {
        return new CosmosProperties();
    }

    @Bean
    public CosmosClientBuilder primaryClientBuilder(@Qualifier("primary") CosmosProperties primaryProperties) {
        return new CosmosClientBuilder()
            .key(primaryProperties.getKey())
            .endpoint(primaryProperties.getUri());
    }

    @Bean
    public ReactiveCosmosTemplate primaryDatabaseTemplate(CosmosAsyncClient cosmosAsyncClient,
                                                          CosmosConfig cosmosConfig,
                                                          MappingCosmosConverter mappingCosmosConverter) {
        return new ReactiveCosmosTemplate(cosmosAsyncClient, PRIMARY_DATABASE, cosmosConfig, mappingCosmosConverter);
    }

    @Override
    protected String getDatabaseName() {
        return PRIMARY_DATABASE;
    }
}
@Configuration
@EnableCosmosRepositories(cosmosTemplateRef  = "secondaryDatabaseTemplate")
public class SecondaryDatasourceConfiguration {

    private static final Logger LOGGER = LoggerFactory.getLogger(SecondaryDatasourceConfiguration.class);
    public static final String SECONDARY_DATABASE = "secondary_database";

    @Bean
    @ConfigurationProperties(prefix = "azure.cosmos.secondary")
    public CosmosProperties secondary() {
        return new CosmosProperties();
    }

    @Bean("secondaryCosmosClient")
    public CosmosAsyncClient getCosmosAsyncClient(@Qualifier("secondary") CosmosProperties secondaryProperties) {
        return CosmosFactory.createCosmosAsyncClient(new CosmosClientBuilder()
            .key(secondaryProperties.getKey())
            .endpoint(secondaryProperties.getUri()));
    }

    @Bean("secondaryCosmosConfig")
    public CosmosConfig getCosmosConfig() {
        return CosmosConfig.builder()
            .enableQueryMetrics(true)
            .responseDiagnosticsProcessor(new ResponseDiagnosticsProcessorImplementation())
            .build();
    }

    @Bean
    public CosmosTemplate secondaryDatabaseTemplate(@Qualifier("secondaryCosmosClient") CosmosAsyncClient client,
                                                    @Qualifier("secondaryCosmosConfig") CosmosConfig cosmosConfig,
                                                    MappingCosmosConverter mappingCosmosConverter) {
        return new CosmosTemplate(client, SECONDARY_DATABASE, cosmosConfig, mappingCosmosConverter);
    }

    private static class ResponseDiagnosticsProcessorImplementation implements ResponseDiagnosticsProcessor {

        @Override
        public void processResponseDiagnostics(@Nullable ResponseDiagnostics responseDiagnostics) {
            LOGGER.info("Response Diagnostics {}", responseDiagnostics);
        }
    }
}

En el ejemplo anterior, tenemos dos cuentas de Cosmos. Puede crear CosmosAsyncClient del modo siguiente:

@Bean("secondaryCosmosClient")
public CosmosAsyncClient getCosmosAsyncClient(@Qualifier("secondary") CosmosProperties secondaryProperties) {
    return CosmosFactory.createCosmosAsyncClient(new CosmosClientBuilder()
        .key(secondaryProperties.getKey())
        .endpoint(secondaryProperties.getUri()));
}

@Bean("secondaryCosmosConfig")
public CosmosConfig getCosmosConfig() {
    return CosmosConfig.builder()
        .enableQueryMetrics(true)
        .responseDiagnosticsProcessor(new ResponseDiagnosticsProcessorImplementation())
        .build();
}

Si quiere definir queryMetricsEnabled o ResponseDiagnosticsProcessor, puede crear CosmosConfig para la plantilla de Cosmos.

@Bean("secondaryCosmosConfig")
public CosmosConfig getCosmosConfig() {
    return CosmosConfig.builder()
        .enableQueryMetrics(true)
        .responseDiagnosticsProcessor(new ResponseDiagnosticsProcessorImplementation())
        .build();
}

Cree una clase Application.

@SpringBootApplication
public class MultiDatabaseApplication implements CommandLineRunner {

    @Autowired
    private CosmosUserRepository cosmosUserRepository;

    @Autowired
    private MysqlUserRepository mysqlUserRepository;

    @Autowired
    @Qualifier("secondaryDatabaseTemplate")
    private CosmosTemplate secondaryDatabaseTemplate;

    @Autowired
    @Qualifier("primaryDatabaseTemplate")
    private ReactiveCosmosTemplate primaryDatabaseTemplate;

    private final CosmosUser cosmosUser = new CosmosUser("1024", "1024@geek.com", "1k", "Mars");
    private static CosmosEntityInformation<CosmosUser, String> userInfo = new CosmosEntityInformation<>(CosmosUser.class);

    public static void main(String[] args) {
        SpringApplication.run(MultiDatabaseApplication.class, args);
    }

    public void run(String... var1) throws Exception {

        CosmosUser cosmosUserGet = primaryDatabaseTemplate.findById(cosmosUser.getId(), cosmosUser.getClass()).block();
        // Same to this.cosmosUserRepository.findById(cosmosUser.getId()).block();
        MysqlUser mysqlUser = new MysqlUser(cosmosUserGet.getId(), cosmosUserGet.getEmail(), cosmosUserGet.getName(), cosmosUserGet.getAddress());
        mysqlUserRepository.save(mysqlUser);
        mysqlUserRepository.findAll().forEach(System.out::println);
        CosmosUser secondaryCosmosUserGet = secondaryDatabaseTemplate.findById(CosmosUser.class.getSimpleName(), cosmosUser.getId(), CosmosUser.class);
        System.out.println(secondaryCosmosUserGet);
    }


    @PostConstruct
    public void setup() {
        primaryDatabaseTemplate.createContainerIfNotExists(userInfo).block();
        primaryDatabaseTemplate.insert(CosmosUser.class.getSimpleName(), cosmosUser, new PartitionKey(cosmosUser.getName())).block();
        // Same to this.cosmosUserRepository.save(user).block();
        secondaryDatabaseTemplate.createContainerIfNotExists(userInfo);
        secondaryDatabaseTemplate.insert(CosmosUser.class.getSimpleName(), cosmosUser, new PartitionKey(cosmosUser.getName()));
   }

    @PreDestroy
    public void cleanup() {
        primaryDatabaseTemplate.deleteAll(CosmosUser.class.getSimpleName(), CosmosUser.class).block();
        // Same to this.cosmosUserRepository.deleteAll().block();
        secondaryDatabaseTemplate.deleteAll(CosmosUser.class.getSimpleName() , CosmosUser.class);
        mysqlUserRepository.deleteAll();
    }
}

Cuenta única con varias bases de datos

En el ejemplo se usa el archivo application.properties:

azure.cosmos.uri=your-cosmosDb-uri
azure.cosmos.key=your-cosmosDb-key
azure.cosmos.secondary-key=your-cosmosDb-secondary-key
azure.cosmos.database=your-cosmosDb-dbName
azure.cosmos.populate-query-metrics=if-populate-query-metrics

La definición de Entity y Repository es similar a la anterior. Puede colocar diferentes entidades de bases de datos en distintos paquetes.

Puede utilizar EnableReactiveCosmosRepositories con diferentes reactiveCosmosTemplateRef para definir varias bases de datos en una única cuenta de Cosmos.

@Configuration
public class DatasourceConfiguration {

    private static final String DATABASE1 = "database1";
    private static final String DATABASE2 = "database2";

    @Bean
    public CosmosProperties cosmosProperties() {
        return new CosmosProperties();
    }

    @Bean
    public CosmosClientBuilder primaryClientBuilder(CosmosProperties cosmosProperties) {
        return new CosmosClientBuilder()
            .key(cosmosProperties.getKey())
            .endpoint(cosmosProperties.getUri());
    }

    @EnableReactiveCosmosRepositories(basePackages = "com.azure.spring.sample.cosmos.multi.database.repository1",
        reactiveCosmosTemplateRef = "database1Template")
    public class Database1Configuration extends AbstractCosmosConfiguration {

        @Bean
        public ReactiveCosmosTemplate database1Template(CosmosAsyncClient cosmosAsyncClient,
                                                              CosmosConfig cosmosConfig,
                                                              MappingCosmosConverter mappingCosmosConverter) {
            return new ReactiveCosmosTemplate(cosmosAsyncClient, DATABASE1, cosmosConfig, mappingCosmosConverter);
        }

        @Override
        protected String getDatabaseName() {
            return DATABASE1;
        }
    }

    @EnableReactiveCosmosRepositories(basePackages = "com.azure.spring.sample.cosmos.multi.database.repository2",
        reactiveCosmosTemplateRef = "database2Template")
    public class Database2Configuration {

        @Bean
        public ReactiveCosmosTemplate database2Template(CosmosAsyncClient cosmosAsyncClient,
                                                              CosmosConfig cosmosConfig,
                                                              MappingCosmosConverter mappingCosmosConverter) {
            return new ReactiveCosmosTemplate(cosmosAsyncClient, DATABASE2, cosmosConfig, mappingCosmosConverter);
        }

    }
}

Cree una clase Application.

@SpringBootApplication
public class MultiDatabaseApplication implements CommandLineRunner {

    @Autowired
    private User1Repository user1Repository;

    @Autowired
    @Qualifier("database1Template")
    private ReactiveCosmosTemplate database1Template;

    @Autowired
    @Qualifier("database2Template")
    private ReactiveCosmosTemplate database2Template;

    private final User1 user1 = new User1("1024", "1024@geek.com", "1k", "Mars");
    private static CosmosEntityInformation<User1, String> user1Info = new CosmosEntityInformation<>(User1.class);

    private final User2 user2 = new User2("2048", "2048@geek.com", "2k", "Mars");
    private static CosmosEntityInformation<User2, String> user2Info = new CosmosEntityInformation<>(User2.class);


    public static void main(String[] args) {
        SpringApplication.run(MultiDatabaseApplication.class, args);
    }

    public void run(String... var1) throws Exception {

        User1 database1UserGet = database1Template.findById(User1.class.getSimpleName(), user1.getId(), User1.class).block();
        // Same to userRepository1.findById(user.getId()).block()
        System.out.println(database1UserGet);
        User2 database2UserGet = database2Template.findById(User2.class.getSimpleName(), user2.getId(), User2.class).block();
        System.out.println(database2UserGet);
    }

    @PostConstruct
    public void setup() {
        database1Template.createContainerIfNotExists(user1Info).block();
        database1Template.insert(User1.class.getSimpleName(), user1, new PartitionKey(user1.getName())).block();
        // Same to this.userRepository1.save(user).block();
        database2Template.createContainerIfNotExists(user2Info).block();
        database2Template.insert(User2.class.getSimpleName(), user2, new PartitionKey(user2.getName())).block();
    }

    @PreDestroy
    public void cleanup() {
        database1Template.deleteAll(User1.class.getSimpleName(), User1.class).block();
        // Same to this.userRepository1.deleteAll().block();
        database2Template.deleteAll(User2.class.getSimpleName(), User2.class).block();
    }
}

Paquete de la versión beta

La versión beta creada a partir de la rama master está disponible; puede consultar la master usar paquetes de versión beta.

Solución de problemas

General

Si encuentra algún error, registre una incidencia aquí.

Para sugerir una nueva característica o cambios que se podrían realizar, registre una incidencia de la misma manera que haría en el caso de un error.

Habilitación del registro de cliente

Azure-spring-data-cosmos utiliza SLF4j como fachada de registro, que admite el registro en plataformas de registro populares como log4j y logback. Por ejemplo, si desea utilizar Spring Logback como plataforma de registro, agregue el siguiente xml a la carpeta de recursos.

<configuration>
  <include resource="/org/springframework/boot/logging/logback/base.xml"/>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
      </pattern>
    </encoder>
  </appender>
  <root level="info">
    <appender-ref ref="STDOUT"/>
  </root>
  <logger name="com.azure.cosmos" level="error"/>
  <logger name="org.springframework" level="error"/>
  <logger name="io.netty" level="error"/>
  <!-- This will enable query logging, to include query parameter logging, set this logger to TRACE -->  
  <logger name="com.azure.cosmos.implementation.SqlQuerySpecLogger" level="DEBUG"/>  
</configuration>

Ejemplos

Consulte el proyecto de ejemplo aquí.

Cuentas de varias bases de datos

Consulte el proyecto de ejemplo de varias bases de datos.

Cuenta única con varias bases de datos

Consulte el proyecto de ejemplo de Cuenta única con varias bases de datos.

Pasos siguientes

Contribuciones

Este proyecto agradece las contribuciones y sugerencias. La mayoría de las contribuciones requieren que acepte un Contrato de licencia para el colaborador (CLA) que declara que tiene el derecho a concedernos y nos concede los derechos para usar su contribución.

Cuando se envía una solicitud de incorporación de cambios, un bot de CLA determinará de forma automática si tiene que aportar un CLA y completar la PR adecuadamente (por ejemplo, la etiqueta, el comentario). Solo siga las instrucciones que le dará el bot. Solo será necesario que lo haga una vez en todos los repositorios con nuestro CLA.

Este proyecto ha adoptado el Código de conducta de Microsoft Open Source. Para más información, consulte las preguntas más frecuentes sobre código de conducta o póngase en contacto con cualquier pregunta o comentario adicional.

Impresiones