你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

适用于 Java 的 Azure Spring Data Cosmos 客户端库 - 版本 5.6.0

Azure Spring Data Cosmos 基于 Spring Data 框架,使用 SQL API 向 Azure Cosmos DB 提供 Spring Data 支持。 Azure Cosmos DB 是一种全球分布式数据库服务,它允许开发人员使用各种标准 API(如 SQL、MongoDB、Cassandra、Graph 和表)处理数据。

Spring Boot 支持策略

此项目支持多个 Spring Boot 版本。 有关当前受支持版本的完整列表,请访问 Spring 版本映射

当 Spring Boot 版本不再受支持或不再以任何形式发布时,它们将标记为“生命周期结束”。 如果你运行的是 EOL 版本,应尽快升级。

请注意,在标记为“生命周期结束”之前,某个版本可能不再受支持。 在此期间,你应仅预期针对严重 bug 或安全问题发布。

有关受支持的 Spring Boot 版本的详细信息,请访问受支持的 Spring Boot 版本

Spring Boot 版本支持

Maven 用户可从 spring-boot-starter-parent 项目继承来获取依赖项管理部分,使 Spring 能够管理依赖项的版本。

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

通过该设置,你还可通过替代你自己的项目中的属性,来替代单个依赖项。 例如,若要升级到其他 Spring Data 版本训练,你要在 pom.xml 中添加以下内容。

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

如果你不想使用 spring-boot-starter-parent,那么你可使用 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>

应使用哪个版本的 Azure Spring Data Cosmos

Spring Boot / Spring Cloud 版本映射到 Azure Spring Data Cosmos 版本

Spring Boot 版本 Spring Cloud 版本 Azure Spring Data Cosmos 版本
3.0.x 2022.0.x 5.3.0 及更高版本
2.7.x 2021.0.x 3.23.0 及更高版本
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

我使用的是 Spring Boot 版本 X

如果在项目中使用 Spring Boot ,可以在上表中找到相关的 Azure Spring Data Cosmos 版本。 例如:如果使用 Spring Boot 3.0.x,则应使用 Azure Spring Data Cosmos 5.3.0 及更高版本。

我使用的是 Spring Cloud 版本 Y

如果在项目中使用 Spring Cloud ,还可以在上表中找到相关的 Azure Spring Data Cosmos 版本。 例如,如果使用 Spring Cloud 2022.0.x,则应使用 Azure Spring Data Cosmos 版本 5.3.0 及更高版本。

Spring Data 版本支持

此项目支持 spring-data-commons 3.0.x 版本。

上述设置不允许使用上述属性替代单个依赖项。 若要获得相同的结果,需要在项目的 dependencyManagement 中的 spring-boot-dependencies 条目前面添加一个条目。 例如,若要升级到其他 Spring Data 版本训练,你要在 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>

注意: 请将 ${spring.boot.version}${spring.data.version} 替换为你想要在项目中使用的 Spring Boot 和 Spring Data 版本。

入门

添加包

如果使用 Maven,请添加以下依赖项。

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

先决条件

仅当计划使用日志记录时,才需要 SLF4J。还请下载 SLF4J 绑定,该绑定可将 SLF4J API 与你选择的记录实现链接在一起。 有关详细信息,请参阅 SLF4J 用户手册

设置配置类

  • 若要设置配置类,需要扩展 AbstractCosmosConfiguration

  • Azure-spring-data-cosmos 还支持 Response Diagnostics StringQuery MetricsMax Degree of Parallelism。 在 application.properties 中将 queryMetricsEnabled 标志设置为 true 以启用查询指标。 除了设置标志,还需要实现 ResponseDiagnosticsProcessor 来记录诊断信息。 在 application.properties 中将标志设置为 maxDegreeOfParallelism 整数以允许并行处理;将值设置为 -1 将导致 SDK 确定最佳值。 在 application.properties 中将标志设置为 maxBufferedItemCount 整数,以允许用户设置在并行查询执行期间可以缓冲的最大项数;如果设置为小于 0,则系统会自动决定要缓冲的项数。 注意:将其设置为非常高的值可能会导致高内存消耗。 在 application.properties 中将标志设置为 responseContinuationTokenLimitInKb 整数,以允许用户限制查询响应中继续标记的长度。 继续标记包含必填字段和可选字段。 必填字段是从停止执行的位置恢复执行所必需的。 可选字段可能包含已完成但尚未利用的序列化索引查找工作。 这可避免在后续延续中再次重做工作,从而提高查询性能。 将最大延续大小设置为 1KB,Azure Cosmos DB 服务将仅序列化必填字段。 从 2KB 开始,Azure Cosmos DB 服务将尽可能多地序列化,直到达到指定的最大大小。 设置 pointOperationLatencyThresholdInMSnonPointOperationLatencyThresholdInMSrequestChargeThresholdInRUpayloadSizeThresholdInBytes ,以便在超过这些阈值时在客户端级别启用诊断。

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

}

自定义配置

可以自定义 DirectConnectionConfig 或或GatewayConnectionConfig两者,并将它们CosmosClientBuilder提供给 bean 来自定义 CosmosAsyncClient 。 可以自定义 pointOperationLatencyThresholdInMSnonPointOperationLatencyThresholdInMSrequestChargeThresholdInRUpayloadSizeThresholdInBytes ,并自定义诊断日志记录的阈值,与 结合使用CosmosDiagnosticsHandler时,在添加到 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();
}

默认情况下,@EnableCosmosRepositories 将扫描当前包中是否有任何接口用来扩展 Spring Data 的某个存储库接口。 如果项目布局有多个项目,则可使用它来注释配置类,使其按 @EnableCosmosRepositories(basePackageClass=UserRepository.class) 扫描不同的根包。

使用 JavaAgent 启用日志记录诊断以Azure 应用程序 Insights

可以通过将 JavaAgent 与应用程序一起传递来启用诊断,如下所示。 这将启用具有默认阈值的日志记录。 “-javaagent”必须在“-jar”之前传递。

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

使用数据库预配的吞吐量

Cosmos 支持 容器数据库 预配的吞吐量。 默认情况下,spring-data-cosmos 将为创建的每个容器预配吞吐量。 如果想要在容器之间共享吞吐量,可以通过 CosmosConfig 启用数据库预配的吞吐量。

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

定义实体

  • 将简单实体定义为 Azure Cosmos DB 中的项。

  • 可通过添加 @Container 注释并指定与容器相关的属性(如容器名称、请求单位 (RU)、生存时间和自动创建容器)来定义实体。

  • 将自动创建容器,除非你禁止。 若要禁止自动创建容器,请在 @Container 中将 autoCreateContainer 设置为 false。

  • 注意:默认情况下,分配给新创建的容器的请求单位为 400。 对于 SDK 创建的容器,指定不同的请求单位 (RU) 值来自定义请求单位(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 字段将用作 Azure Cosmos DB 中的项 ID。 如果要使用其他字段(如 firstName )作为项目 id,只需使用 @Id 批注为该字段添加批注即可。

  • 注释 @Container(containerName="myContainer") 在 Azure Cosmos DB 中指定容器名称。

  • lastName 字段上的注释 @PartitionKey 在 Azure Cosmos DB 中将此字段指定为分区键。

创建具有自动缩放吞吐量的容器

  • 注释 autoScale 字段指定要在设置为 true 时创建的带自动缩放吞吐量的容器。 默认值为 false,这意味着会创建带有手动吞吐量的容器。
  • 请在此处详细了解自动缩放吞吐量
@Container(containerName = "myContainer", autoScale = true, ru = "4000")
public class UserSample {
    @Id
    private String emailAddress;

}

嵌套分区键支持

  • Azure Spring Data Cosmos 支持嵌套分区键。 若要添加嵌套分区键,请使用 @Container 注释中的 partitionKeyPath 字段。
  • partitionKeyPath 只应用于支持嵌套分区键路径。 对于常规分区键支持,请使用 @PartitionKey 注释。
  • 默认情况下,将优先采用 @PartitionKey 注释(除非未指定)。
  • 下面的示例演示如何正确使用嵌套分区键功能。
@Container(containerName = "nested-partition-key", partitionKeyPath = "/nestedEntitySample/nestedPartitionKey")
public class NestedPartitionKeyEntitySample {

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

创建存储库

扩展了 CosmosRepository 接口,它提供 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);
}
  • findByFirstName 方法是自定义查询方法,它将按 firstName 查找项。

查询计划缓存

Spring 存储库查询 API(例如 findByFirstName(String firstName) ,其中 firstName 是分区或包含分区键的带批注的查询)将导致查询执行时间降低,因为查询计划缓存。 目前,仅针对单个分区的查询方法支持查询计划缓存。

QueryAnnotation:在存储库中使用带注释的查询

Azure Spring Data Cosmos 支持使用 @Query 在存储库中指定带注释的查询。

  • 同步的 CosmosRepository 中带注释的查询示例:
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);
}
  • 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);
}

注释中指定的查询与 Cosmos 查询相同。 若要详细了解 Cosmos 中的 SQL 查询,请查看以下文章

创建应用程序类

下面创建一个带有所有组件的应用程序类

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

    }
}
  • Autowire UserRepository 接口,用于执行保存、删除、查找等操作
  • Spring Data Azure Cosmos DB 使用 CosmosTemplateReactiveCosmosTemplate 在查找和保存方法后面执行查询 。 可自行使用模板来执行更复杂的查询。

关键概念

CrudRepository 和 ReactiveCrudRepository

  • Azure Spring Data Cosmos 支持 ReactiveCrudRepository 和 CrudRepository,后者提供基本 CRUD 功能
    • 保存
    • findAll
    • 按 ID 查找一个
    • deleteAll
    • 按 ID 删除
    • delete entity

Spring Data 注释

Spring Data @Id 注释

有两种方法将域类中的字段映射到 Azure Cosmos DB 项的 id 字段。

  • 使用 @Id 注释域类中的字段,该字段将映射到 Cosmos DB 中的项 id
  • 将该字段的名称设置为 id,该字段将映射到 Cosmos DB 中的项 id

ID 自动生成

  • 支持使用 @GeneratedValue 注释自动生成字符串类型 UUID。 可以使用 对具有字符串类型 ID 的实体的 id 字段进行 @GeneratedValue 批注,以在插入之前自动生成随机 UUID。
public class GeneratedIdEntity {

    @Id
    @GeneratedValue
    private String id;

}

SpEL 表达式和自定义容器名称。

  • 默认情况下,容器名称将是用户域类的类名。 若要对其进行自定义,请将 @Container(containerName="myCustomContainerName") 批注添加到域类中。 容器字段还支持 SpEL 表达式 (例如 container = "${dynamic.container.name}"container = "#{@someBean.getContainerName()}") ,以便以编程方式/通过配置属性提供容器名称。
  • 为了使 SpEL 表达式正常工作,需要在 Spring 应用程序类之上添加 @DependsOn("expressionResolver")
@SpringBootApplication
@DependsOn("expressionResolver")
public class SampleApplication {
    
}

索引策略

  • 默认情况下,IndexingPolicy 将由 Azure 门户服务设置。 若要对其进行自定义,请将注释 @CosmosIndexingPolicy 添加到域类中。 此批注有 5 个要自定义的属性,请参阅以下内容:
// 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 {};

唯一密钥策略

  • Azure Spring Data Cosmos 支持 UniqueKeyPolicy 通过将 注释 @CosmosUniqueKeyPolicy 添加到域类来设置容器。 此批注具有以下属性:
@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;
}

Azure Cosmos DB 分区

  • Azure-spring-data-cosmos 支持 Azure Cosmos DB 分区
  • 若要指定域类的字段作为分区键字段,只需使用 @PartitionKey 来批注它即可。
  • 执行 CRUD 操作时,指定分区值。
  • 有关分区 CRUD 的更多示例,请参阅此处的测试

乐观锁定

  • Azure-spring-data-cosmos 支持对特定容器使用乐观锁定,这意味着按项更新插入/删除将失败,除非该项同时在被另一进程修改。
  • 若要为容器启用乐观锁定,只需创建一个字符串 _etag 字段,然后使用 @Version 注释标记它即可。 参阅以下内容:
@Container(containerName = "myContainer")
public class MyItem {
    String id;
    String data;
    @Version
    String _etag;
}
  • 在此处阅读有关乐观锁定的详细信息

Spring Data 自定义查询,可分页和排序

  • Azure-spring-data-cosmos 支持 Spring Data 自定义查询
  • 例如,findByAFieldAndBField 等查找操作
  • 支持 Spring Data 可分页、切片和排序
    • 根据数据库帐户上提供的 RU,cosmosDB 可能返回小于或等于请求的大小的项。
    • 由于每次迭代中返回的项的数目可变,用户不得依赖于 totalPageSize,而应按这种方式循环访问可分页项。
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 静态数据

  • Azure-spring-data-cosmos 支持 spring-boot-starter-data-rest
  • 支持域类中的列表和嵌套类型。
  • 具有唯一名称 cosmosObjectMapper的可配置 ObjectMapper bean,仅在确实需要时才配置自定义 ObjectMapper。例如,
@Bean(name = "cosmosObjectMapper")
public ObjectMapper objectMapper() {
    return new ObjectMapper(); // Do configuration to the ObjectMapper if required
}

审核

  • Azure-spring-data-cosmos 支持使用标准的 spring-data 注释审核关于数据库实体的字段。
  • 可通过向应用程序配置添加 @EnableCosmosAuditing 注释来启用此功能。
  • 实体可使用 @CreatedBy@CreatedDate@LastModifiedBy@LastModifiedDate 对字段进行批注。 这些字段将自动更新。
@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;
}

多数据库配置

  • Azure-spring-data-cosmos 支持多数据库配置,包括“多个数据库帐户”和“带有多个数据库的单个帐户”。

多数据库帐户

示例使用 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
  • 实体存储库定义与上面的类似。 可将不同的数据库实体放入不同的包中。

  • @EnableReactiveCosmosRepositories@EnableCosmosRepositories 支持由用户定义 Cosmos 模板,使用 reactiveCosmosTemplateRefcosmosTemplateRef 配置 ReactiveCosmosTemplateCosmosTemplate bean 的名称来与删除的存储库一起使用。

  • 如果你有多个 Cosmos 数据库帐户,可定义多个 CosmosAsyncClient。 如果单个 Cosmos 帐户有多个数据库,则可使用相同的 CosmosAsyncClient 来初始化 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);
        }
    }
}
  • 在上例中,我们有 2 个 Cosmos 帐户。 可创建 CosmosAsyncClient,如下所示:
@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();
}
  • 此外,如果要定义 queryMetricsEnabledResponseDiagnosticsProcessormaxDegreeOfParallelismmaxBufferedItemCountresponseContinuationTokenLimitInKb ,可以为 cosmos 模板创建 CosmosConfig
@Bean("secondaryCosmosConfig")
public CosmosConfig getCosmosConfig() {
    return CosmosConfig.builder()
        .enableQueryMetrics(true)
        .maxDegreeOfParallelism(0)
        .maxBufferedItemCount(0)
        .responseContinuationTokenLimitInKb(0)
        .responseDiagnosticsProcessor(new ResponseDiagnosticsProcessorImplementation())
        .build();
}
  • 创建应用程序类
@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();
    }
}

具有多个数据库的单个帐户

示例使用 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
  • 实体存储库定义与上面的类似。 可将不同的数据库实体放入不同的包中。
  • 可将 EnableReactiveCosmosRepositories 与不同的 reactiveCosmosTemplateRef 相结合,在一个 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);
        }

    }
}
  • 创建应用程序类
@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();
    }
}

数据库级别的多租户

  • Azure-spring-data-cosmos 通过扩展 CosmosFactory 和重写 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();
    }
}

Beta 版本包

提供根据 main 分支构建的 Beta 版本,你可参考说明来使用 beta 版本包。

疑难解答

常规

如果遇到任何 bug,请在此处提交问题。

若要建议新功能或可以进行的更改,请以与提交 bug 相同的方式提交问题。

启用客户端日志记录

  • Azure-spring-data-cosmos 使用 SLF4j 作为日志外观,支持记录到 log4j 和 logback 等常用的日志框架。 例如,如果你想要将 spring logback 用作日志框架,请在资源文件夹中添加以下 xml。
<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>

示例

多数据库帐户

具有多个数据库的单个帐户

后续步骤

贡献

本项目欢迎贡献和建议。 大多数贡献要求你同意贡献者许可协议 (CLA),并声明你有权(并且确实有权)授予我们使用你的贡献的权利。

提交拉取请求时,CLA 机器人将自动确定你是否需要提供 CLA,并相应地修饰 PR(例如标签、注释)。 直接按机器人提供的说明操作。 只需使用 CLA 对所有存储库执行一次这样的操作。

此项目采用了 Microsoft 开放源代码行为准则。 有关详细信息,请参阅行为准则常见问题解答,或如果有任何其他问题或意见,请与 联系。

曝光数