This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
Note
Access to this page requires authorization. You can trysigning in orchanging directories.
Access to this page requires authorization. You can trychanging directories.
Azure Spring Data Cosmos provides Spring Data support for Azure Cosmos DB using theSQL API, based on Spring Data framework.Azure Cosmos DB is a globally-distributed database service which allows developers to work with data using a variety of standard APIs, such as SQL, MongoDB, Cassandra, Graph, and Table.
This project supports multipleSpring Boot Versions. For complete list of currently supported versions, please visit ourSpring Version Mapping.
Spring Boot releases are marked as "End of Life" when they are no longer supported or released in any form. If you are running an EOL version, you should upgrade as soon as possible.
Please note that a version can be out of support before it is marked as "End of Life". During this time you should only expect releases for critical bugs or security issues.
For more information on Spring Boot supported versions, please visitSpring Boot Supported Versions.
Maven users can inherit from thespring-boot-starter-parent
project to obtain a dependency management section to let Spring manage the versions for dependencies.
<!-- Inherit defaults from Spring Boot --><parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>${spring.boot.version}</version></parent>
With that setup, you can also override individual dependencies by overriding a property in your own project. For instance, to upgrade to another Spring Data release train you’d add the following to your pom.xml.
<properties> <spring-data-releasetrain.version>${spring.data.version}</spring-data-releasetrain.version></properties>
If you don’t want to use thespring-boot-starter-parent
, you can still keep the benefit of the dependency management by using ascope=import
dependency:
<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>
Mapping fromSpring Boot /Spring Cloud version toAzure Spring Data Cosmos versions
Spring Boot version | Spring Cloud version | Azure Spring Data Cosmos versions |
---|---|---|
3.0.x | 2022.0.x | 5.3.0 and above |
2.7.x | 2021.0.x | 3.23.0 and above |
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 |
If you are usingSpring Boot in your project, you can find relatedAzure Spring Data Cosmos versions from above table. For example: if you are usingSpring Boot 2.7.x, you should useAzure Spring Data Cosmos versions 3.23.0 and above.
If you are usingSpring Cloud in your project, you can also find relatedAzure Spring Data Cosmos versions from above table. For example, if you are usingSpring Cloud 2021.0.x, you should useAzure Spring Data Cosmos versions 3.23.0 and above.
This project supportsspring-data-commons 2.7.x
versions.
The above setup does not allow you to override individual dependencies using a property as explained above. To achieve the same result, you’d need to add an entry in the dependencyManagement of your project before thespring-boot-dependencies
entry. For instance, to upgrade to another Spring Data release train you’d add the following to your 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>
Note: Replace the${spring.boot.version} and${spring.data.version} with the versions of Spring Boot and Spring Data you want to use in your project.
If you are using Maven, add the following dependency.
<dependency> <groupId>com.azure</groupId> <artifactId>azure-spring-data-cosmos</artifactId> <version>3.47.0</version></dependency>
SLF4J is only needed if you plan to use logging, please also download an SLF4J binding which will link the SLF4J API with the logging implementation of your choice. See theSLF4J user manual for more information.
In order to set up configuration class, you'll need to extendAbstractCosmosConfiguration
Azure-spring-data-cosmos also supportsResponse Diagnostics String
,Query Metrics
,Index Metrics
andMax Degree of Parallelism
.SetqueryMetricsEnabled
flag to true in application.properties to enable query metrics.SetindexMetricsEnabled
flag to true in application.properties to enable index metrics.In addition to setting the flag, implementResponseDiagnosticsProcessor
to log diagnostics information.SetmaxDegreeOfParallelism
flag to an integer in application.properties to allow parallel processing; setting the value to -1 will lead to the SDK deciding the optimal value.SetmaxBufferedItemCount
flag to an integer in application.properties to allow the user to set the max number of items that can be buffered during parallel query execution; if set to less than 0, the system automatically decides the number of items to buffer.NOTE: Setting this to a very high value can result in high memory consumption.SetresponseContinuationTokenLimitInKb
flag to an integer in application.properties to allow the user to limit the length of the continuation token in the query response. The continuation token contains both required and optional fields. The required fields are necessary for resuming the execution from where it was stoped. The optional fields may contain serialized index lookup work that was done but not yet utilized. This avoids redoing the work again in subsequent continuations and hence improve the query performance. Setting the maximum continuation size to 1KB, the Azure Cosmos DB service will only serialize required fields. Starting from 2KB, the Azure Cosmos DB service would serialize as much as it could fit till it reaches the maximum specified size.SetpointOperationLatencyThresholdInMS
,nonPointOperationLatencyThresholdInMS
,requestChargeThresholdInRU
andpayloadSizeThresholdInBytes
to enable diagnostics at the client level when these thresholds are exceeded.
@Configuration@EnableCosmosRepositoriespublic 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.indexMetricsEnabled}") private boolean indexMetricsEnabled; @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) .enableIndexMetrics(indexMetricsEnabled) .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); } }}
You can customizeDirectConnectionConfig
orGatewayConnectionConfig
or both and provide them toCosmosClientBuilder
bean to customizeCosmosAsyncClient
You can customizepointOperationLatencyThresholdInMS
,nonPointOperationLatencyThresholdInMS
,requestChargeThresholdInRU
andpayloadSizeThresholdInBytes
to customize the thresholds for diagnostic logging when combined withCosmosDiagnosticsHandler
which enables diagnostic logging with the default thresholds when added to theCosmosClientBuilder
.
@Beanpublic 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));}@Overridepublic CosmosConfig cosmosConfig() { return CosmosConfig.builder() .enableQueryMetrics(queryMetricsEnabled) .enableIndexMetrics(indexMetricsEnabled) .maxDegreeOfParallelism(maxDegreeOfParallelism) .maxBufferedItemCount(maxBufferedItemCount) .responseContinuationTokenLimitInKb(responseContinuationTokenLimitInKb) .responseDiagnosticsProcessor(new ResponseDiagnosticsProcessorImplementation()) .build();}
By default,@EnableCosmosRepositories
will scan the current package for any interfaces that extend one of Spring Data's repository interfaces.Use it to annotate your Configuration class to scan a different root package by@EnableCosmosRepositories(basePackageClass=UserRepository.class)
if your project layout has multiple projects.
Diagnostics can be enabled by passing the JavaAgent with your application like below. This will enable logging with the default thresholds. The '-javaagent' must be passed before the '-jar'.
java -javaagent:"<path-to-applicationinsights-agent-jar>" -jar <myapp.jar>
Cosmos supports bothcontaineranddatabase provisionedthroughput. By default, spring-data-cosmos will provision throughput for each container created. If you preferto share throughput between containers, you can enable database provisioned throughput via CosmosConfig.
@Overridepublic CosmosConfig cosmosConfig() { int autoscale = false; int initialRequestUnits = 400; return CosmosConfig.builder() .enableDatabaseThroughput(autoscale, initialRequestUnits) .build();}
Define a simple entity as item in Azure Cosmos DB.
You can define entities by adding the@Container
annotation and specifying properties related to the container, such as the container name, request units (RUs), time to live, and auto-create container.
Containers will be created automatically unless you don't want them to. SetautoCreateContainer
to false in@Container
annotation to disable auto creation of containers.
Note: If you are using provisioned throughput, you can optionally specify different ru values to customize request units for the container created by the SDK. The minimum ru should be 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
field will be used as Item id in Azure Cosmos DB. If you want use another field likefirstName
as itemid
, just annotate that field with@Id
annotation.
Annotation@Container(containerName="myContainer")
specifies container name in Azure Cosmos DB.
Annotation@PartitionKey
onlastName
field specifies this field as partition key in Azure Cosmos DB.
autoScale
field specifies container to be created with autoscale throughput if set to true. Default is false, which means containers are created with manual throughput.@Container(containerName = "myContainer", autoScale = true, ru = "4000")public class UserSample { @Id private String emailAddress;}
partitionKeyPath
field in@Container
annotation.partitionKeyPath
should only be used to support nested partition key path. For general partition key support, use the@PartitionKey
annotation.@PartitionKey
annotation will take precedence, unless not specified.@Container(containerName = "nested-partition-key", partitionKeyPath = "/nestedEntitySample/nestedPartitionKey")public class NestedPartitionKeyEntitySample { private NestedEntitySample nestedEntitySample;}
public class NestedEntitySample { private String nestedPartitionKey;}
Extends CosmosRepository interface, which provides Spring Data repository support.
@Repositorypublic interface UserRepository extends CosmosRepository<User, String> { Iterable<User> findByFirstName(String firstName); long countByFirstName(String firstName); User findOne(String id, String lastName);}
findByFirstName
method is custom query method, it will find items per firstName.Spring repository query APIs likefindByFirstName(String firstName)
wherefirstName
is the partition or annotated queries containing partition key will result in lower query execution time because of query plan caching. Currently, query plan caching is only supported for query methods targeting a single partition.
Azure spring data cosmos supports specifying annotated queries in the repositories using@Query
.
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);}
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);}
The queries that are specified in the annotation are same as the cosmos queries.Please refer to the following articles for more information on sql queries in cosmos
Here create an application class with all the components
@SpringBootApplicationpublic 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); }}
CosmosTemplate
andReactiveCosmosTemplate
to execute the queries behindfind,save methods. You can use the template yourself for more complex queries.There are 2 ways to map a field in domain class toid
field of Azure Cosmos DB Item.
@Id
, this field will be mapped to Itemid
in Cosmos DB.id
, this field will be mapped to Itemid
in Azure Cosmos DB.@GeneratedValue
to automatically generate a random UUID prior to insertion.public class GeneratedIdEntity { @Id @GeneratedValue private String id;}
@Container(containerName="myCustomContainerName")
annotation to the domain class. The container field also supports SpEL expressions (eg.container = "${dynamic.container.name}"
orcontainer = "#{@someBean.getContainerName()}"
) in order to provide container names programmatically/via configuration properties.@DependsOn("expressionResolver")
on top of Spring Application class.@SpringBootApplication@DependsOn("expressionResolver")public class SampleApplication { }
@CosmosIndexingPolicy
to domain class. This annotation has 5 attributes to customize, see following:// Indicate if indexing policy use automatic or not// Default value is trueboolean automatic() default Constants.DEFAULT_INDEXING_POLICY_AUTOMATIC;// Indexing policy mode, option Consistent.IndexingMode mode() default IndexingMode.CONSISTENT;// Included paths for indexingString[] includePaths() default {};// Excluded paths for indexingString[] excludePaths() default {};
UniqueKeyPolicy
on container by adding the annotation@CosmosUniqueKeyPolicy
to domain class. This annotation has the following attributes:@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;}
@PartitionKey
._etag
field and mark it with the@Version
annotation. See the following:@Container(containerName = "myContainer")public class MyItem { String id; String data; @Version String _etag;}
findByAFieldAndBField
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;}
CosmosClient
orCosmosAsyncClient
bean throughApplicationContext
and execute any operations supported by Azure Cosmos DB Java SDK.@SpringBootApplicationpublic class CosmosClientBeanCodeSnippet { @Autowired private ApplicationContext applicationContext; public void cosmosClientBean() { CosmosClient cosmosClient = applicationContext.getBean(CosmosClient.class); CosmosContainer myContainer = cosmosClient.getDatabase("myDatabase").getContainer("myContainer"); // Creating a stored procedure myContainer.getScripts().createStoredProcedure( new CosmosStoredProcedureProperties("storedProcedureId", "function(){}"), new CosmosStoredProcedureRequestOptions()); // Reading a stored procedure myContainer.getScripts().getStoredProcedure("storedProcedureId").read(); } public void cosmosAsyncClientBean() { CosmosAsyncClient cosmosAsyncClient = applicationContext.getBean(CosmosAsyncClient.class); CosmosAsyncContainer myAsyncContainer = cosmosAsyncClient.getDatabase("myDatabase").getContainer("myContainer"); // Creating a stored procedure myAsyncContainer.getScripts().createStoredProcedure( new CosmosStoredProcedureProperties("storedProcedureId", "function(){}"), new CosmosStoredProcedureRequestOptions()).subscribe(); // Reading a stored procedure myAsyncContainer.getScripts().getStoredProcedure("storedProcedureId").read().subscribe(); }}
cosmosObjectMapper
, only configure customized ObjectMapper if you really need to. e.g.,@Bean(name = "cosmosObjectMapper")public ObjectMapper objectMapper() { return new ObjectMapper(); // Do configuration to the ObjectMapper if required}
@EnableCosmosAuditing
annotation to your application configuration.@CreatedBy
,@CreatedDate
,@LastModifiedBy
and@LastModifiedDate
. These fields will be updated automatically.@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;}
The example uses theapplication.properties
file
# primary account cosmos configazure.cosmos.primary.uri=your-primary-cosmosDb-uriazure.cosmos.primary.key=your-primary-cosmosDb-keyazure.cosmos.primary.secondaryKey=your-primary-cosmosDb-secondary-keyazure.cosmos.primary.database=your-primary-cosmosDb-dbNameazure.cosmos.primary.populateQueryMetrics=if-populate-query-metricsazure.cosmos.primary.populateIndexMetrics=if-populate-index-metrics# secondary account cosmos configazure.cosmos.secondary.uri=your-secondary-cosmosDb-uriazure.cosmos.secondary.key=your-secondary-cosmosDb-keyazure.cosmos.secondary.secondaryKey=your-secondary-cosmosDb-secondary-keyazure.cosmos.secondary.database=your-secondary-cosmosDb-dbNameazure.cosmos.secondary.populateQueryMetrics=if-populate-query-metricsazure.cosmos.secondary.populateIndexMetrics=if-populate-index-metrics
TheEntity andRepository definition is similar as above. You can put different database entities into different packages.
The@EnableReactiveCosmosRepositories
or@EnableCosmosRepositories
support user-define the cosmos template, usereactiveCosmosTemplateRef
orcosmosTemplateRef
to config the name of theReactiveCosmosTemplate
orCosmosTemplate
bean to be used with the repositories detected.
If you have multiple cosmos database accounts, you can define multipleCosmosAsyncClient
. If the single cosmos account has multiple databases, you can use the sameCosmosAsyncClient
to initialize the cosmos template.
@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) .enableIndexMetrics(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); } }}
CosmosAsyncClient
like this:@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) .enableIndexMetrics(true) .maxDegreeOfParallelism(0) .maxBufferedItemCount(0) .responseContinuationTokenLimitInKb(0) .responseDiagnosticsProcessor(new ResponseDiagnosticsProcessorImplementation()) .build();}
queryMetricsEnabled
,indexMetricsEnabled
,ResponseDiagnosticsProcessor
,maxDegreeOfParallelism
,maxBufferedItemCount
orresponseContinuationTokenLimitInKb
, you can create theCosmosConfig
for your cosmos template.@Bean("secondaryCosmosConfig")public CosmosConfig getCosmosConfig() { return CosmosConfig.builder() .enableQueryMetrics(true) .enableIndexMetrics(true) .maxDegreeOfParallelism(0) .maxBufferedItemCount(0) .responseContinuationTokenLimitInKb(0) .responseDiagnosticsProcessor(new ResponseDiagnosticsProcessorImplementation()) .build();}
@SpringBootApplicationpublic 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(); }}
The example uses theapplication.properties
file
azure.cosmos.uri=your-cosmosDb-uriazure.cosmos.key=your-cosmosDb-keyazure.cosmos.secondary-key=your-cosmosDb-secondary-keyazure.cosmos.database=your-cosmosDb-dbNameazure.cosmos.populate-query-metrics=if-populate-query-metrics
EnableReactiveCosmosRepositories
with differentreactiveCosmosTemplateRef
to define multiple databases in single cosmos account.@Configurationpublic 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); } }}
@SpringBootApplicationpublic 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(); }}
CosmosFactory
and overriding the getDatabaseName() function.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 version built frommain
branch are available, you can refer to theinstruction to use beta version packages.
If you encounter any bug, please file an issuehere.
To suggest a new feature or changes that could be made, file an issue the same way you would for a bug.
<configuration> <include resource="/org/springframework/boot/logging/logback/base.xml"/> <appender name="STDOUT"> <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>
This project welcomes contributions and suggestions. Most contributions require you to agree to aContributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rightsto use your contribution.
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decoratethe PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need todo this once across all repos using our CLA.
This project has adopted theMicrosoft Open Source Code of Conduct. For more information see theCode of Conduct FAQor contactopencode@microsoft.com with any additional questions or comments.
Was this page helpful?
Was this page helpful?