See: Description
| Package | Description |
|---|---|
| com.azure.spring.data.cosmos |
Cosmosdb class for spring
|
| com.azure.spring.data.cosmos.common |
This package contains the classes of utils for cosmosdb
|
| com.azure.spring.data.cosmos.config |
This package contains the classes to configure properties of cosmos db
|
| com.azure.spring.data.cosmos.core |
This package contains the core classes of cosmos db, includes converters,
query generators and mapping to cosmos entities
|
| com.azure.spring.data.cosmos.core.convert |
This package contains the converter classes of cosmos db
|
| com.azure.spring.data.cosmos.core.generator |
This package contains the generator classes of cosmos db
|
| com.azure.spring.data.cosmos.core.mapping |
This package contains the mapping classes of cosmos persistent entities
|
| com.azure.spring.data.cosmos.core.query |
This package contains the query classes of cosmos db document
|
| com.azure.spring.data.cosmos.exception |
This package contains the exception classes of cosmos db
|
| com.azure.spring.data.cosmos.repository |
This package contains the support, query and config classes of setting up cosmosdb repositories
|
| com.azure.spring.data.cosmos.repository.config |
This package contains the config classes of setting up cosmosdb repositories
|
| com.azure.spring.data.cosmos.repository.query |
This package contains the process cosmos queries
|
| com.azure.spring.data.cosmos.repository.support |
This package contains the support classes of setting up cosmosdb repositories and factories
|
Azure Spring Data Cosmos provides Spring Data support for Azure Cosmos DB using the SQL 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 both spring-data-commons 2.2.x and spring-data-commons 2.3.x versions. Maven users can inherit from the spring-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 the spring-boot-starter-parent, you can still keep the benefit of the dependency management by using a scope=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>
That 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 the spring-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.4.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 the SLF4J user manual for more information.
In order to set up configuration class, you'll need to extend AbstractCosmosConfiguration
Azure-spring-data-cosmos also supports Response Diagnostics String and Query Metrics.
Set queryMetricsEnabled flag to true in application.properties to enable query metrics.
In addition to setting the flag, implement ResponseDiagnosticsProcessor to log diagnostics information.
@Configuration
@EnableCosmosRepositories
public class AppConfiguration extends AbstractCosmosConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(AppConfiguration.class);
@Value("${azure.cosmos.uri}")
private String uri;
@Value("${azure.cosmos.key}")
private String key;
@Value("${azure.cosmos.secondaryKey}")
private String secondaryKey;
@Value("${azure.cosmos.database}")
private String dbName;
@Value("${azure.cosmos.queryMetricsEnabled}")
private boolean queryMetricsEnabled;
private AzureKeyCredential azureKeyCredential;
@Bean
public CosmosClientBuilder getCosmosClientBuilder() {
this.azureKeyCredential = new AzureKeyCredential(key);
DirectConnectionConfig directConnectionConfig = new DirectConnectionConfig();
GatewayConnectionConfig gatewayConnectionConfig = new GatewayConnectionConfig();
return new CosmosClientBuilder()
.endpoint(uri)
.credential(azureKeyCredential)
.directMode(directConnectionConfig, gatewayConnectionConfig);
}
@Override
public CosmosConfig cosmosConfig() {
return CosmosConfig.builder()
.enableQueryMetrics(queryMetricsEnabled)
.responseDiagnosticsProcessor(new ResponseDiagnosticsProcessorImplementation())
.build();
}
public void switchToSecondaryKey() {
this.azureKeyCredential.update(secondaryKey);
}
@Override
protected String getDatabaseName() {
return "testdb";
}
private static class ResponseDiagnosticsProcessorImplementation implements ResponseDiagnosticsProcessor {
@Override
public void processResponseDiagnostics( @Nullable ResponseDiagnostics responseDiagnostics) {
LOGGER.info("Response Diagnostics {}", responseDiagnostics);
}
}
}
You can customize DirectConnectionConfig or GatewayConnectionConfig or both and provide them to CosmosClientBuilder bean to customize CosmosAsyncClient
@Bean
public CosmosClientBuilder getCosmosClientBuilder() {
DirectConnectionConfig directConnectionConfig = new DirectConnectionConfig();
GatewayConnectionConfig gatewayConnectionConfig = new GatewayConnectionConfig();
return new CosmosClientBuilder()
.endpoint(uri)
.directMode(directConnectionConfig, gatewayConnectionConfig);
}
@Override
public CosmosConfig cosmosConfig() {
return CosmosConfig.builder()
.enableQueryMetrics(queryMetricsEnabled)
.responseDiagnosticsProcessor(new ResponseDiagnosticsProcessorImplementation())
.build();
}
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.
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. Set autoCreateContainer to false in @Container annotation to disable auto creation of containers.
Note: By default request units assigned to newly created containers is 400. Specify different ru value to customize request units for the container created by the SDK (minimum RU value is 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 like firstName as item id, just annotate that field with @Id annotation.
Annotation @Container(containerName="myContainer") specifies container name in Azure Cosmos DB.
@PartitionKey on lastName field specifies this field as partition key in Azure Cosmos DB.
@Container(containerName = "myContainer")
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.
@Repository
public interface UserRepository extends CosmosRepository<User, String> {
Iterable<User> findByFirstName(String firstName);
User findOne(String id, String lastName);
}
findByFirstName method is custom query method, it will find items per firstName.Azure spring data cosmos supports specifying annotated queries in the repositories using @Query.
- Examples for annotated queries in synchronous CosmosRepository:
public interface AnnotatedQueriesUserRepositoryCodeSnippet extends CosmosRepository<User, String> {
@Query(value = "select * from c where c.firstName = @firstName and c.lastName = @lastName")
List<User> getUsersByFirstNameAndLastName( @Param("firstName") String firstName, @Param("lastName") String lastName);
@Query(value = "select * from c offset @offset limit @limit")
List<User> getUsersWithOffsetLimit( @Param("offset") int offset, @Param("limit") int limit);
}
public interface AnnotatedQueriesUserReactiveRepositoryCodeSnippet extends ReactiveCosmosRepository<User, String> {
@Query(value = "select * from c where c.firstName = @firstName and c.lastName = @lastName")
Flux<User> getUsersByTitleAndValue( @Param("firstName") int firstName, @Param("lastName") String lastName);
@Query(value = "select * from c offset @offset limit @limit")
Flux<User> getUsersWithOffsetLimit( @Param("offset") int offset, @Param("limit") int limit);
@Query(value = "select count(c.id) as num_ids, c.lastName from c group by c.lastName")
Flux<ObjectNode> getCoursesGroupByDepartment();
}
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 - sql-query-getting-started - tutorial-query-sql-api
Here create an application class with all the components
@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);
}
}
CosmosTemplate and ReactiveCosmosTemplate to execute the queries behind find, save methods. You can use the template yourself for more complex queries.id field of Azure Cosmos DB Item.
@Id, this field will be mapped to Item id in Cosmos DB.id, this field will be mapped to Item id in Azure Cosmos DB.Supports auto generation of string type UUIDs using the @GeneratedValue annotation. The id field of an entity with a string
type id can be annotated with @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}" or container = "#{@someBean.getContainerName()}") in order to provide container names programmatically/via configuration properties.@CosmosIndexingPolicy to domain class. This annotation has 4 attributes to customize, see following:
// 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 {};
@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;
}
findByAFieldAndBFieldprivate 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;
}
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 the application.properties file
# 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
The Entity and Repository definition is similar as above. You can put different database entities into different packages.
The @EnableReactiveCosmosRepositories or @EnableCosmosRepositories support user-define the cosmos template, use reactiveCosmosTemplateRef or cosmosTemplateRef to config the name of the ReactiveCosmosTemplate or CosmosTemplate bean to be used with the repositories detected.
CosmosAsyncClient. If the single cosmos account has multiple databases, you can use the same CosmosAsyncClient to initialize the cosmos template. @Configuration
public class PrimaryDatasourceConfiguration {
private static final String DATABASE1 = "primary_database1";
private static final String DATABASE2 = "primary_database2";
@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());
}
@EnableReactiveCosmosRepositories(basePackages = "com.azure.cosmos.multidatasource.primary.database1")
public class DataBase1Configuration extends AbstractCosmosConfiguration {
@Override
protected String getDatabaseName() {
return DATABASE1;
}
}
@EnableReactiveCosmosRepositories(basePackages = "com.azure.cosmos.multidatasource.primary.database2",
reactiveCosmosTemplateRef = "primaryDatabase2Template")
public class Database2Configuration {
@Bean
public ReactiveCosmosTemplate primaryDatabase2Template(CosmosAsyncClient cosmosAsyncClient,
CosmosConfig cosmosConfig,
MappingCosmosConverter mappingCosmosConverter) {
return new ReactiveCosmosTemplate(cosmosAsyncClient, DATABASE2, cosmosConfig, mappingCosmosConverter);
}
}
}
@Configuration
public class SecondaryDatasourceConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(SecondaryDatasourceConfiguration.class);
public static final String DATABASE3 = "secondary_database3";
public static final String DATABASE4 = "secondary_database4";
@Bean
@ConfigurationProperties(prefix = "azure.cosmos.secondary")
public CosmosProperties secondary() {
return new CosmosProperties();
}
@Bean("secondaryCosmosClient")
public CosmosAsyncClient getCosmosAsyncClient( @Qualifier("secondary") CosmosProperties secondaryProperties) {
return CosmosFactory.createCosmosAsyncClient(new CosmosClientBuilder()
.key(secondaryProperties.getKey())
.endpoint(secondaryProperties.getUri()));
}
@Bean("secondaryCosmosConfig")
public CosmosConfig getCosmosConfig() {
return CosmosConfig.builder()
.enableQueryMetrics(true)
.responseDiagnosticsProcessor(new ResponseDiagnosticsProcessorImplementation())
.build();
}
@EnableCosmosRepositories(basePackages = "com.azure.cosmos.multidatasource.secondary.database3",
cosmosTemplateRef = "secondaryDatabase3Template")
public class Database3Configuration {
@Bean
public CosmosTemplate secondaryDatabase3Template( @Qualifier("secondaryCosmosClient") CosmosAsyncClient client,
@Qualifier("secondaryCosmosConfig") CosmosConfig cosmosConfig,
MappingCosmosConverter mappingCosmosConverter) {
return new CosmosTemplate(client, DATABASE3, cosmosConfig, mappingCosmosConverter);
}
}
@EnableCosmosRepositories(basePackages = "com.azure.cosmos.multidatasource.secondary.database4",
cosmosTemplateRef = "secondaryDatabase4Template")
public class Database4Configuration {
@Bean
public CosmosTemplate secondaryDatabase4Template( @Qualifier("secondaryCosmosClient") CosmosAsyncClient client,
@Qualifier("secondaryCosmosConfig") CosmosConfig cosmosConfig,
MappingCosmosConverter mappingCosmosConverter) {
return new CosmosTemplate(client, DATABASE4, 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)
.responseDiagnosticsProcessor(new ResponseDiagnosticsProcessorImplementation())
.build();
}
@EnableCosmosRepositories(basePackages = "com.azure.cosmos.multidatasource.secondary.database3",
queryMetricsEnabled or ResponseDiagnosticsProcessor , you can create the CosmosConfig for your cosmos template. @Bean("secondaryCosmosConfig")
public CosmosConfig getCosmosConfig() {
return CosmosConfig.builder()
.enableQueryMetrics(true)
.responseDiagnosticsProcessor(new ResponseDiagnosticsProcessorImplementation())
.build();
}
@SpringBootApplication
public class MultiDatasourceApplication implements CommandLineRunner {
@Autowired
private UserRepository userRepository;
@Autowired
private BookRepository bookRepository;
private final User user = new User("1024", "1024 @geek.com", "1k", "Mars");
private final Book book = new Book("9780792745488", "Zen and the Art of Motorcycle Maintenance", "Robert M. Pirsig");
public static void main(String[] args) {
SpringApplication.run(MultiDatasourceApplication.class, args);
}
@Override
public void run(String... args) {
final List<User> users = this.userRepository.findByEmailOrName(this.user.getEmail(), this.user.getName()).collectList().block();
users.forEach(System.out::println);
final Book book = this.bookRepository.findById("9780792745488").block();
System.out.println(book);
}
@PostConstruct
public void setup() {
this.userRepository.save(user).block();
this.bookRepository.save(book).block();
}
@PreDestroy
public void cleanup() {
this.userRepository.deleteAll().block();
this.bookRepository.deleteAll().block();
}
}
Beta version built from master branch are available, you can refer to the instruction to use beta version packages.
If you encounter any bug, please file an issue here.
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" 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"/>
</configuration>
This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution.
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.

Copyright © 2021 Microsoft Corporation. All rights reserved.