Biblioteca de clientes do Azure Spring Data Cosmos para Java – versão 5.6.0

O Azure Spring Data Cosmos dá suporte ao Spring Data para o Azure Cosmos DB usando a API SQL, com base na estrutura do Spring Data. O Azure Cosmos DB é um serviço de banco de dados distribuído globalmente que permite aos desenvolvedores trabalhar com os dados usando várias APIs padrão, como SQL, MongoDB, Cassandra, Graph e de Tabela.

Política de suporte do Spring Boot

Este projeto dá suporte a várias versões do Spring Boot. Para obter uma lista completa das versões suportadas no momento, visite nosso Mapeamento de versão do Spring.

As versões do Spring Boot estão marcadas como "Fim da Vida" quando não têm mais suporte ou são lançadas de forma alguma. Se você estiver executando uma versão do EOL, deverá atualizar o mais rápido possível.

Observe que uma versão pode ficar sem suporte antes de ser marcada como "Fim da Vida Útil". Durante esse tempo, você deve esperar apenas versões para bugs críticos ou problemas de segurança.

Para obter mais informações sobre as versões com suporte do Spring Boot, visite versões com suporte do Spring Boot.

Suporte à versão do Spring Boot

Os usuários do Maven podem herdar do projeto spring-boot-starter-parent para obter uma seção de gerenciamento de dependência para permitir que o Spring gerencie as versões para as dependências.

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

Com essa configuração, você também pode substituir dependências individuais substituindo uma propriedade no seu próprio projeto. Por exemplo, para atualizar para outro treinamento de versão do Spring Data, adicione o item a seguir ao seu pom.xml.

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

Se você não quiser usar o spring-boot-starter-parent, ainda poderá manter o benefício do gerenciamento de dependência usando uma dependência 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>

Qual versão do Azure Spring Data Cosmos devo usar

Mapeamento da versão do SpringCloud do Spring Boot / para as versões do Azure Spring Data Cosmos

Versão do Spring Boot Versão do Spring Cloud Versões do Azure Spring Data Cosmos
3.0.x 2022.0.x 5.3.0 e superior
2.7.x 2021.0.x 3.23.0 e superior
2.6.x 2021.0.x 3.15.0 - 3.22.0
2.5.x 2020.0.x 3.8.0 - 3.14.0
2.4.x 2020.0.x 3.5.0 - 3.7.0

Estou usando a versão X do Spring Boot

Se você estiver usando o Spring Boot em seu projeto, poderá encontrar versões relacionadas do Azure Spring Data Cosmos da tabela acima. Por exemplo: se você estiver usando o Spring Boot 3.0.x, deverá usar o Azure Spring Data Cosmos versões 5.3.0 e superiores.

Estou usando a versão Y do Spring Cloud

Se você estiver usando o Spring Cloud em seu projeto, também poderá encontrar versões relacionadas do Azure Spring Data Cosmos da tabela acima. Por exemplo, se você estiver usando o Spring Cloud 2022.0.x, deverá usar o Azure Spring Data Cosmos versões 5.3.0 e superior.

Suporte à versão do Spring Data

Este projeto dá suporte a versões spring-data-commons 3.0.x .

A configuração acima não permite que você substitua dependências individuais usando uma propriedade, conforme explicado acima. Para obter o mesmo resultado, você precisaria adicionar uma entrada na dependencyManagement do projeto antes da entrada spring-boot-dependencies. Por exemplo, para atualizar para outro treinamento de versão do Spring Data, adicione o item a seguir ao seu 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>

Observação: substitua ${spring.boot.version} e ${spring.data.version} por versões do Spring Boot e do Spring Data que você deseja usar em seu projeto.

Introdução

Incluir o pacote

Caso esteja usando o Maven, adicione a dependência a seguir.

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

Pré-requisitos

  • JDK (Java Development Kit) versão 8 ou posterior.
  • Uma conta ativa do Azure. Se não tiver uma, você poderá se inscrever em uma conta gratuita. Como alternativa, você pode usar o Emulador do Azure Cosmos DB para desenvolvimento e teste. Como o certificado https do emulador é autoassinado, você precisa importar seu certificado para o repositório de certificados confiável java, explicado aqui
  • (Opcional) SLF4J é uma fachada de registro em log.
  • (Opcional) A associação SLF4J é usada para associar uma estrutura de registro em log específica ao SLF4J.
  • (Opcional) Maven

O SLF4J só será necessário se você planejar usar o registro em log. Baixe também uma associação SLF4J que vinculará a API SLF4J à implementação do registro em log de sua escolha. Para obter mais informações, confira o Manual do usuário do SLF4J.

Definir classe de configuração

  • Para definir a classe de configuração, você precisará estender o AbstractCosmosConfiguration

  • O Azure-spring-data-cosmos também dá Response Diagnostics Stringsuporte a e Query MetricsMax Degree of Parallelism. Defina o sinalizador queryMetricsEnabled como true em application.properties para habilitar as métricas de consulta. Além de definir o sinalizador, implemente o ResponseDiagnosticsProcessor para registrar informações de diagnóstico. Defina maxDegreeOfParallelism o sinalizador como um inteiro em application.properties para permitir o processamento paralelo; definir o valor como -1 levará o SDK a decidir o valor ideal. Defina maxBufferedItemCount o sinalizador como um inteiro em application.properties para permitir que o usuário defina o número máximo de itens que podem ser armazenados em buffer durante a execução de consulta paralela; se definido como menor que 0, o sistema decide automaticamente o número de itens para buffer. OBSERVAÇÃO: definir isso como um valor muito alto pode resultar em alto consumo de memória. Defina responseContinuationTokenLimitInKb o sinalizador como um inteiro em application.properties para permitir que o usuário limite o comprimento do token de continuação na resposta da consulta. O token de continuação contém campos obrigatórios e opcionais. Os campos necessários são necessários para retomar a execução de onde ela foi interrompida. Os campos opcionais podem conter o trabalho de pesquisa de índice serializado que foi feito, mas ainda não utilizado. Isso evita refazer o trabalho novamente em continuações subsequentes e, portanto, melhorar o desempenho da consulta. Definindo o tamanho máximo de continuação como 1KB, o serviço do Azure Cosmos DB só serializará os campos necessários. A partir de 2KB, o serviço do Azure Cosmos DB seria serializado o máximo que pudesse caber até atingir o tamanho máximo especificado. Defina pointOperationLatencyThresholdInMS, requestChargeThresholdInRUnonPointOperationLatencyThresholdInMSe payloadSizeThresholdInBytes para habilitar diagnóstico no nível do cliente quando esses limites forem excedidos.

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

    @Value("${azure.cosmos.maxDegreeOfParallelism}")
    private int maxDegreeOfParallelism;

    @Value("${azure.cosmos.maxBufferedItemCount}")
    private int maxBufferedItemCount;

    @Value("${azure.cosmos.responseContinuationTokenLimitInKb}")
    private int responseContinuationTokenLimitInKb;

    @Value("${azure.cosmos.diagnosticsThresholds.pointOperationLatencyThresholdInMS}")
    private int pointOperationLatencyThresholdInMS;

    @Value("${azure.cosmos.diagnosticsThresholds.nonPointOperationLatencyThresholdInMS}")
    private int nonPointOperationLatencyThresholdInMS;

    @Value("${azure.cosmos.diagnosticsThresholds.requestChargeThresholdInRU}")
    private int requestChargeThresholdInRU;

    @Value("${azure.cosmos.diagnosticsThresholds.payloadSizeThresholdInBytes}")
    private int payloadSizeThresholdInBytes;


    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)
            .clientTelemetryConfig(
                new CosmosClientTelemetryConfig()
                    .diagnosticsThresholds(
                        new CosmosDiagnosticsThresholds()
                            .setNonPointOperationLatencyThreshold(Duration.ofMillis(nonPointOperationLatencyThresholdInMS))
                            .setPointOperationLatencyThreshold(Duration.ofMillis(pointOperationLatencyThresholdInMS))
                            .setPayloadSizeThreshold(payloadSizeThresholdInBytes)
                            .setRequestChargeThreshold(requestChargeThresholdInRU)
                    )
                    .diagnosticsHandler(CosmosDiagnosticsHandler.DEFAULT_LOGGING_HANDLER));
    }

    @Override
    public CosmosConfig cosmosConfig() {
        return CosmosConfig.builder()
                           .enableQueryMetrics(queryMetricsEnabled)
                           .maxDegreeOfParallelism(maxDegreeOfParallelism)
                           .maxBufferedItemCount(maxBufferedItemCount)
                           .responseContinuationTokenLimitInKb(responseContinuationTokenLimitInKb)
                           .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);
        }
    }

}

Personalizando a Configuração

Você pode personalizar DirectConnectionConfig ou GatewayConnectionConfig ou ambos e fornecê-los para CosmosClientBuilder personalizar CosmosAsyncClient Você pode personalizar pointOperationLatencyThresholdInMS, requestChargeThresholdInRUnonPointOperationLatencyThresholdInMSe payloadSizeThresholdInBytes personalizar os limites para registro em log de diagnóstico quando combinado com CosmosDiagnosticsHandler o que permite o registro em log de diagnóstico com os limites padrão quando adicionado ao CosmosClientBuilder.

@Bean
public CosmosClientBuilder getCosmosClientBuilder() {

    DirectConnectionConfig directConnectionConfig = new DirectConnectionConfig();
    GatewayConnectionConfig gatewayConnectionConfig = new GatewayConnectionConfig();
    return new CosmosClientBuilder()
        .endpoint(uri)
        .directMode(directConnectionConfig, gatewayConnectionConfig)
        .clientTelemetryConfig(
            new CosmosClientTelemetryConfig()
                .diagnosticsThresholds(
                    new CosmosDiagnosticsThresholds()
                        .setNonPointOperationLatencyThreshold(Duration.ofMillis(nonPointOperationLatencyThresholdInMS))
                        .setPointOperationLatencyThreshold(Duration.ofMillis(pointOperationLatencyThresholdInMS))
                        .setPayloadSizeThreshold(payloadSizeThresholdInBytes)
                        .setRequestChargeThreshold(requestChargeThresholdInRU)
                )
                .diagnosticsHandler(CosmosDiagnosticsHandler.DEFAULT_LOGGING_HANDLER));
}

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

Por padrão, o @EnableCosmosRepositories verificará o pacote atual em busca de interfaces que estendam uma das interfaces de repositório do Spring Data. Use-o para anotar sua classe de Configuração para verificar um pacote raiz diferente pelo @EnableCosmosRepositories(basePackageClass=UserRepository.class) se o layout do projeto tiver vários projetos.

Habilitar diagnóstico de log para Aplicativo Azure Insights com JavaAgent

O diagnóstico pode ser habilitado passando o JavaAgent com seu aplicativo, como abaixo. Isso habilitará o registro em log com os limites padrão. O '-javaagent' deve ser passado antes do '-jar'.

java -javaagent:"<path-to-applicationinsights-agent-jar>" -jar <myapp.jar>

Usando a taxa de transferência provisionada do banco de dados

O Cosmos dá suporte à taxa de transferência provisionada por contêiner e banco de dados . Por padrão, o spring-data-cosmos provisionará a taxa de transferência para cada contêiner criado. Se preferir compartilhar a taxa de transferência entre contêineres, você poderá habilitar a taxa de transferência provisionada do banco de dados por meio do CosmosConfig.

@Override
public CosmosConfig cosmosConfig() {
    int autoscale = false; 
    int initialRequestUnits = 400;
    return CosmosConfig.builder()
                       .enableDatabaseThroughput(autoscale, initialRequestUnits) 
                       .build();
}

Definir uma entidade

  • Defina uma entidade simples como item no Azure Cosmos DB.

  • Você pode definir entidades adicionando a anotação @Container e especificando propriedades relacionadas ao contêiner, como o nome do contêiner, as RUs (unidades de solicitação), a vida útil e o contêiner de criação automática.

  • Os contêineres serão criados automaticamente, a menos que você não queira. Defina o autoCreateContainer como false na anotação @Container para desabilitar a criação automática de contêineres.

  • Observação: por padrão, as unidades de solicitação atribuídas a contêineres recém-criados são 400. Especifique um valor de RU diferente para personalizar unidades de solicitação para o contêiner criado pelo SDK (o valor mínimo de RU é 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;
    }
}
  • id O campo será usado como id de item no Azure Cosmos DB. Se você quiser usar outro campo como firstName item id, basta anotar esse campo com @Id anotação.

  • A anotação @Container(containerName="myContainer") especifica o nome do contêiner no Azure Cosmos DB.

  • A anotação @PartitionKey no campo lastName especifica esse campo como a chave de partição no Azure Cosmos DB.

Criando Contêineres com taxa de transferência de dimensionamento automático

  • O campo autoScale da anotação especifica o contêiner a ser criado com a taxa de transferência de dimensionamento automático, se definido como true. O padrão é false, o que significa que os contêineres são criados com taxa de transferência manual.
  • Leia mais sobre a taxa de transferência de dimensionamento automático aqui
@Container(containerName = "myContainer", autoScale = true, ru = "4000")
public class UserSample {
    @Id
    private String emailAddress;

}

Suporte à Chave de Partição Aninhada

  • O Azure Spring Data Cosmos dá suporte à chave de partição aninhada. Para adicionar a chave de partição aninhada, use o campo partitionKeyPath na anotação @Container.
  • partitionKeyPath só deve ser usado para dar suporte ao caminho da chave de partição aninhada. Para suporte geral à chave de partição, use a anotação @PartitionKey.
  • Por padrão, a anotação @PartitionKey terá precedência, a menos que não seja especificado.
  • O exemplo abaixo mostra como usar corretamente o recurso chave de Partição Aninhada.
@Container(containerName = "nested-partition-key", partitionKeyPath = "/nestedEntitySample/nestedPartitionKey")
public class NestedPartitionKeyEntitySample {

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

Criar repositórios

Estende a interface do CosmosRepository, que dá suporte ao repositório do 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);
}
  • O método findByFirstName é um método de consulta personalizado, ele encontrará itens por firstName.

Cache de plano de consulta

AS APIs de consulta do repositório Spring, como findByFirstName(String firstName) em que firstName é a partição ou consultas anotadas que contêm chave de partição, resultarão em um tempo de execução de consulta inferior devido ao cache do plano de consulta. Atualmente, o cache do plano de consulta só tem suporte para métodos de consulta direcionados a uma única partição.

QueryAnnotation: usando consultas anotadas em repositórios

O Azure Spring Data Cosmos dá suporte à especificação de consultas anotadas nos repositórios usando @Query.

  • Exemplos de consultas anotadas no CosmosRepository síncrono:
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);
}
  • Exemplos de consultas anotadas no 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);
}

As consultas que são especificadas na anotação são as mesmas que as consultas do cosmos. Confira os artigos a seguir para obter mais informações sobre consultas sql no cosmos

Criar uma Classe de aplicativos

Aqui, crie uma classe de aplicativos com todos os 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);

    }
}
  • Interface UserRepository de fio automático, para executar operações como salvar, excluir, encontrar etc.
  • O Azure Cosmos DB do Spring Data usa o CosmosTemplate e o ReactiveCosmosTemplate para executar as consultas por trás dos métodos encontrar e salvar. Você pode usar o modelo por conta própria para consultas mais complexas.

Principais conceitos

Suporte a CrudRepository e a ReactiveCrudRepository

  • O Azure Spring Data Cosmos dá suporte a ReactiveCrudRepository e CrudRepository, que fornece funcionalidade CRUD básica
    • Salvar
    • findAll
    • findOne por Id
    • deleteAll
    • excluir por ID
    • excluir entidade

Anotações do Spring Data

Anotação de @Id do Spring Data

Há duas maneiras de mapear um campo na classe de domínio para o campo id do Item do Azure Cosmos DB.

  • anotar um campo na classe de domínio com @Id, esse campo será mapeado para Item id no Cosmos DB.
  • definir o nome desse campo como id, esse campo será mapeado para o Item id no Azure Cosmos DB.

Geração automática de ID

  • Dá suporte à geração automática de UUIDs de tipo de cadeia de caracteres usando a anotação @GeneratedValue. O campo ID de uma entidade com uma ID de tipo de cadeia de caracteres pode ser anotado com @GeneratedValue para gerar automaticamente um UUID aleatório antes da inserção.
public class GeneratedIdEntity {

    @Id
    @GeneratedValue
    private String id;

}

Expressão SpEL e Nome de Contêiner Personalizado.

  • Por padrão, o nome do contêiner será o nome de classe da classe de domínio de usuário. Para personalizá-lo, adicione a anotação @Container(containerName="myCustomContainerName") à classe de domínio. O campo de contêiner também dá suporte a expressões SpEL (por exemplo, container = "${dynamic.container.name}" ou container = "#{@someBean.getContainerName()}") para fornecer nomes de contêiner programaticamente/por meio de propriedades de configuração.
  • Para que as expressões SpEL funcionem corretamente, você precisa adicionar @DependsOn("expressionResolver") na parte superior da classe do Spring Application.
@SpringBootApplication
@DependsOn("expressionResolver")
public class SampleApplication {
    
}

Política de indexação

  • Por padrão, IndexingPolicy será definido pelo Serviço do Portal do Azure. Para personalizá-la, adicione a anotação @CosmosIndexingPolicy à classe de domínio. Essa anotação tem 5 atributos para personalizar, confira a seguir:
// 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 {};

Política de Chave Exclusiva

  • O Azure Spring Data Cosmos dá suporte à configuração UniqueKeyPolicy no contêiner adicionando a anotação @CosmosUniqueKeyPolicy à classe de domínio. Essa anotação tem os seguintes atributos:
@Container
@CosmosUniqueKeyPolicy(uniqueKeys = {
    @CosmosUniqueKey(paths = {"/lastName", "/zipCode"}),
    @CosmosUniqueKey(paths = {"/city"})
})
public class CosmosUniqueKeyPolicyCodeSnippet {

    @Id
    String id;

    @PartitionKey
    String firstName;

    String lastName;
    String zipCode;
    String city;
}

Partição do Azure Cosmos DB

  • O Azure-spring-data-cosmos dá suporte à partição do Azure Cosmos DB.
  • Para especificar um campo de uma classe de domínio como o campo da chave da partição, anote-o com @PartitionKey.
  • Ao executar a operação CRUD, especifique o valor da partição.
  • Para obter mais amostras sobre a CRUD de partição, confira o teste aqui

Bloqueio Otimista

  • O Azure-spring-data-cosmos dá suporte ao Bloqueio Otimista para contêineres específicos, o que significa que upserts/exclusões por item falharão com uma exceção caso o item seja modificado por outro processo nesse meio tempo.
  • Para habilitar o Bloqueio Otimista para um contêiner, basta criar um campo de cadeia de caracteres _etag e marcá-lo com a anotação @Version. Veja o seguinte:
@Container(containerName = "myContainer")
public class MyItem {
    String id;
    String data;
    @Version
    String _etag;
}
  • Leia mais sobre bloqueio otimista aqui

Consulta personalizada do Spring Data, paginável e classificação

  • O Azure-spring-data-cosmos dá suporte a consultas personalizadas do Spring Data
  • Por exemplo, operação find, por exemplo, findByAFieldAndBField
  • suporte a Paginável, Fatia e Classificação do Spring Data.
    • Com base nas RUs disponíveis na conta do banco de dados, o cosmosDB pode retornar itens menores ou iguais ao tamanho solicitado.
    • Devido a esse número variável de itens retornados em cada iteração, o usuário não deve depender do totalPageSize e, em vez disso, a iteração sobre o paginável deve ser feita dessa maneira.
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;
}

Dados Inativos do Iniciador do Spring Boot

  • O Azure-spring-data-cosmos dá suporte a spring-boot-starter-data-rest.
  • Dá suporte à Lista e ao tipo aninhado na classe de domínio.
  • Feijão ObjectMapper configurável com nome cosmosObjectMapperexclusivo , configure somente ObjectMapper personalizado se você realmente precisar. Por exemplo,
@Bean(name = "cosmosObjectMapper")
public ObjectMapper objectMapper() {
    return new ObjectMapper(); // Do configuration to the ObjectMapper if required
}

Auditoria

  • O Azure-spring-data-cosmos dá suporte a campos de auditoria em entidades do banco de dados usando anotações spring-data padrão.
  • Esse recurso pode ser habilitado adicionando a anotação @EnableCosmosAuditing à configuração do aplicativo.
  • As entidades podem anotar campos usando @CreatedBy, @CreatedDate, @LastModifiedBy e @LastModifiedDate. Esses campos serão atualizados automaticamente.
@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;
}

Configuração de vários bancos de dados

  • O Azure-spring-data-cosmos dá suporte à configuração de vários bancos de dados, incluindo "várias contas de banco de dados" e "conta única, com vários bancos de dados".

Contas de vários bancos de dados

O exemplo usa o arquivo 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
  • A definição de Entidade e Repositório é semelhante à acima. Você pode colocar entidades do banco de dados diferentes em pacotes diferentes.

  • O usuário de suporte @EnableReactiveCosmosRepositories ou @EnableCosmosRepositories definem o modelo do cosmos, use reactiveCosmosTemplateRef ou cosmosTemplateRef para configurar o nome do bean ReactiveCosmosTemplate ou CosmosTemplate a ser usado com os repositórios detectados.

  • Se você tiver várias contas de banco de dados do cosmos, poderá definir vários CosmosAsyncClient. Se a única conta do cosmos tiver vários bancos de dados, você poderá usar o mesmo CosmosAsyncClient para inicializar o modelo do 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)
            .maxDegreeOfParallelism(0)
            .maxBufferedItemCount(0)
            .responseContinuationTokenLimitInKb(0)
            .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);
        }
    }
}
  • No exemplo acima, temos duas contas do Cosmos. Você pode criar o CosmosAsyncClient da mesma forma:
@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)
        .maxDegreeOfParallelism(0)
        .maxBufferedItemCount(0)
        .responseContinuationTokenLimitInKb(0)
        .responseDiagnosticsProcessor(new ResponseDiagnosticsProcessorImplementation())
        .build();
}
  • Além disso, se você quiser definir queryMetricsEnabled, ResponseDiagnosticsProcessor, maxDegreeOfParallelismmaxBufferedItemCount ou responseContinuationTokenLimitInKb , poderá criar o para o CosmosConfig modelo cosmos.
@Bean("secondaryCosmosConfig")
public CosmosConfig getCosmosConfig() {
    return CosmosConfig.builder()
        .enableQueryMetrics(true)
        .maxDegreeOfParallelism(0)
        .maxBufferedItemCount(0)
        .responseContinuationTokenLimitInKb(0)
        .responseDiagnosticsProcessor(new ResponseDiagnosticsProcessorImplementation())
        .build();
}
  • Criar uma Classe de aplicativos
@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();
    }
}

Conta única com vários bancos de dados

O exemplo usa o arquivo 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
  • A definição de Entidade e Repositório é semelhante à acima. Você pode colocar entidades do banco de dados diferentes em pacotes diferentes.
  • Você pode usar o EnableReactiveCosmosRepositories com diferentes reactiveCosmosTemplateRef para definir vários bancos de dados em uma única conta do 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);
        }

    }
}
  • Criar uma Classe de aplicativos
@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();
    }
}

Multilocação no nível do banco de dados

  • O Azure-spring-data-cosmos dá suporte à multilocação na configuração de nível de banco de dados estendendo CosmosFactory e substituindo a função getDatabaseName().
public class MultiTenantDBCosmosFactory extends CosmosFactory {

    private String tenantId;

    /**
     * Validate config and initialization
     *
     * @param cosmosAsyncClient cosmosAsyncClient
     * @param databaseName      databaseName
     */
    public MultiTenantDBCosmosFactory(CosmosAsyncClient cosmosAsyncClient, String databaseName) {
        super(cosmosAsyncClient, databaseName);

        this.tenantId = databaseName;
    }

    @Override
    public String getDatabaseName() {
        return this.getCosmosAsyncClient().getDatabase(this.tenantId).toString();
    }
}

Pacote da versão beta

A versão beta criada da ramificação main está disponível. Você pode conferir a instrução para usar pacotes de versão beta.

Solução de problemas

Geral

Se encontrar algum bug, registre um problema aqui.

Para sugerir um novo recurso ou alterações possíveis, envie um problema da mesma maneira que faria para um bug.

Habilitar o Registro em log do Cliente

  • O Azure-spring-data-cosmos usa SLF4j como a fachada de registro em log que dá suporte ao registro em log em estruturas de registro em log populares, como log4j e logback. Por exemplo, se você quiser usar o logback do spring como estrutura de registro em log, adicione o xml a seguir à pasta 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>

Exemplos

Contas de vários bancos de dados

Conta única com vários bancos de dados

Próximas etapas

Contribuição

Este projeto aceita contribuições e sugestões. A maioria das contribuições exige que você concorde com um CLA (Contrato de Licença do Colaborador) declarando que você tem o direito de nos conceder e de fato concede, os direitos de usar sua contribuição.

Quando você envia uma solicitação de pull, um bot do CLA determina automaticamente se você precisa fornecer um CLA e preencher a PR corretamente (por exemplo, rótulo, comentário). Basta seguir as instruções fornecidas pelo bot. Você só precisará fazer isso uma vez em todos os repositórios que usam nosso CLA.

Este projeto adotou o Código de Conduta de Software Livre da Microsoft. Para obter mais informações, confira as Perguntas frequentes sobre o Código de Conduta ou contate opencode@microsoft.com para enviar outras perguntas ou comentários.

Impressões