Technical Documentation: spring-projects/spring-data-examples
โน๏ธ Provenance: Multi-Tier Ingestion ยท CodeWiki Reference
Overview (`multi-store/README.md`)
Spring Data - Multi-store example
This sample shows a project working with multiple Spring Data modules and how the repository auto-detection has become stricter with the Evans release train.
If you run ApplicationConfigurationTest you should see the following output:
โฆ DEBUG โฆ - Multiple Spring Data modules found, entering strict repository configuration mode!
โฆ
โฆ DEBUG โฆ - Spring Data JPA - Could not safely identify store assignment for repository candidate interface example.springdata.multistore.shop.OrderRepository.
โฆ DEBUG โฆ - Spring Data JPA - Registering repository: customerRepository - Interface: example.springdata.multistore.customer.CustomerRepository - Factory: org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean
โฆ DEBUG โฆ - Multiple Spring Data modules found, entering strict repository configuration mode!
โฆ
โฆ DEBUG โฆ - Spring Data MongoDB - Could not safely identify store assignment for repository candidate interface example.springdata.multistore.customer.CustomerRepository.
โฆ DEBUG โฆ - Spring Data MongoDB - Registering repository: orderRepository - Interface: example.springdata.multistore.shop.OrderRepository - Factory: org.springframework.data.mongodb.repository.support.MongoRepositoryFactoryBeanAs you can see, Spring Data detects the fact that the application runs on multiple Spring Data modules. This triggers the strict configuration mode in which only repository interfaces will be detected that can be uniquely assigned to the module currently scanned. By default this assignment is detected by inspecting the managed domain type for store specific annotations (e.g. @Entity for JPA or @Document for MongoDB).
Overview (`cassandra/kotlin/README.md`)
Spring Data Cassandra - Kotlin examples
This project contains samples of Kotlin-specific features of Spring Data (Cassandra).
Value defaulting on entity construction
Kotlin allows defaulting for constructor- and method arguments.
Defaulting allows usage of substitute values if a field in the document is absent or simply null.
Spring Data inspects objects whether they are Kotlin types and uses the appropriate constructor.
@Table
data class Person(@PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED) val firstname: String? = "", val lastname: String = "White")
operations.cqlOperations.execute(QueryBuilder.insertInto("person").value("firstname", "Walter"))
val person = operations.query<Person>()
.matching(query(where("firstname").isEqualTo("Walter")))
.firstValue()!!
assertThat(person.lastname).isEqualTo("White")Kotlin Extensions
Spring Data exposes methods accepting a target type to either query for or to project results values on.
Kotlin represents classes with its own type, KClass which can be an obstacle when attempting to obtain a Java Class type.
Spring Data ships with extensions that add overloads for methods accepting a type parameter by either leveraging generics or accepting KClass directly.
operations.getTableName<Person>()
operations.getTableName(Person::class)Nullability
Declaring repository interfaces using Kotlin allows expressing nullability constraints on arguments and return types. Spring Data evaluates nullability of arguments and return types and reacts to these. Passing null to a non-nullable argument raises an IllegalArgumentException, as you're already used to from Kotlin. Spring Data helps you also to prevent null in query results. If you wish to return a nullable result, use Kotlin's nullability marker ?. To prevent null results, declare the return type of a query method as non-nullable. In the case a query yields no result, a non-nullable query method throws EmptyResultDataAccessException.
interface PersonRepository : CrudRepository<Person, String> {
/**
* Query method declaring a nullable return type that allows to return null values.
*/
fun findOneOrNoneByFirstname(firstname: String): Person?
/**
* Query method declaring a nullable argument.
*/
fun findNullableByFirstname(firstname: String?): Person?
/**
* Query method requiring a result. Throws [org.springframework.dao.EmptyResultDataAccessException] if no result is found.
*/
fun findOneByFirstname(firstname: String): Person
}Overview (`cassandra/reactive/README.md`)
Spring Data Cassandra 2.0 - Reactive examples
This project contains samples of reactive data access features with Spring Data (Cassandra).
Reactive Template API usage with `ReactiveCassandraTemplate`
The main reactive Template API class is ReactiveCassandraTemplate, ideally used through its interface ReactiveCassandraOperations. It defines a basic set of reactive data access operations using Project Reactor Mono and Flux reactive types.
template.insert(Flux.just(new Person("Walter", "White", 50),
new Person("Skyler", "White", 45),
new Person("Saul", "Goodman", 42),
new Person("Jesse", "Pinkman", 27)));
Flux<Person> flux = template.select(select()
.from("person")
.where(eq("lastname", "White")), Person.class);The test cases in ReactiveCassandraTemplateIntegrationTest show basic Template API usage.
Reactive data access reads and converts individual elements while processing the stream.
Reactive Repository support
Spring Data Cassandra provides reactive repository support with Project Reactor and RxJava 1 reactive types. The reactive API supports reactive type conversion between reactive types.
public interface ReactivePersonRepository extends ReactiveCrudRepository<Person, String> {
Flux<Person> findByLastname(String lastname);
@Query("SELECT * FROM person WHERE firstname = ?0 and lastname = ?1")
Mono<Person> findByFirstnameAndLastname(String firstname, String lastname);
// Accept parameter inside a reactive type for deferred execution
Flux<Person> findByLastname(Mono<String> lastname);
Mono<Person> findByFirstnameAndLastname(Mono<String> firstname, String lastname);
}public interface RxJava1PersonRepository extends RxJava1CrudRepository<Person, String> {
Observable<Person> findByLastname(String lastname);
@Query("SELECT * FROM person WHERE firstname = ?0 and lastname = ?1")
Single<Person> findByFirstnameAndLastname(String firstname, String lastname);
// Accept parameter inside a reactive type for deferred execution
Observable<Person> findByLastname(Single<String> lastname);
Single<Person> findByFirstnameAndLastname(Single<String> firstname, String lastname);
}Overview (`cassandra/vector-search/README.md`)
Spring Data for Apache Cassandra - Vector Search Example
This project
contains Vector Search
with Spring Data for Apache Cassandra.
Vector Support
The Spring Data Vector type can be used in repository query methods.
Domain type properties of managed domain types are required to use a numeric array representation for embeddings.
@Table
public class Comment {
@Id
private String id;
private String country;
private String description;
@SaiIndexed
@VectorType(dimensions = 5)
private Vector embedding;
// ...
}
public interface CommentRepository extends Repository<Comment, String> {
SearchResults<Comment> searchTop10ByEmbeddingNear(Vector embedding, ScoringFunction function);
}This example contains a test class to illustrate vector search with a Repository in CassandraVectorSearchTest.
Overview (`couchbase/example/README.md`)
Spring Data Couchbase - Examples
This project contains samples of data access features with Spring Data (Couchbase).
Prerequisites
The examples require a running Couchbase Server with the travel sample bucket imported. We assume you're running Couchbase 6.5 and we have updated the application.properties class accordingly to adapt RBAC authentication.
For more information, see the official documentation.
Overview (`couchbase/reactive/README.md`)
Spring Data Couchbase 4.0 - Reactive examples
This project contains samples of reactive data access features with Spring Data (Couchbase).
Reactive Repository support
Spring Data Couchbase provides reactive repository support with Project Reactor:
public interface ReactiveAirlineRepository extends ReactiveCrudRepository<Airline, String> {
Mono<Airline> findByIata(String code);
Flux<Airline> findAllBy();
}For more information, see the official documentation.
Overview (`couchbase/transactions/README.md`)
Getting Started
Running the Sample
$> docker run -d --name db -p 8091-8097:8091-8097 -p 11210:11210 -p 11207:11207 -p 18091-18095:18091-18095 -p 18096:18096 -p 18097:18097 couchbase- Create new cluster via
http://localhost:8091/ - Create new bucket named travel-sample.
Reference Documentation
For further reference, please consider the following sections:
- Official Apache Maven documentation
- Spring Boot Maven Plugin Reference Guide
- Create an OCI image
- Spring Data Couchbase
Overview (`elasticsearch/example/README.md`)
Spring Data Elasticsearch - Examples
Requirements:
- Maven - required.
- Elasticsearch - optional.
To use local elasticsearch cluster:
- install elasticsearch
- Adapt 'application.properties' to point to your elasticsearch cluster
Overview (`elasticsearch/reactive/README.md`)
Spring Data Elasticsearch - Reactive Examples
The ReactiveElasticsearchClient is a client based on WebClient.
It uses the request/response objects provided by the Elasticsearch core project.
Calls are directly operated on the reactive stack, not wrapping async (thread pool bound) responses into reactive types.
@SpringBootApplication
class ApplicationConfiguration {}The ReactiveElasticsearchClient can be used with the ReactiveElasticsearchOperations and ReactiveElasticsearchRepository.
@Autowired ReactiveElasticsearchOperations operations;
// ...
CriteriaQuery query = new CriteriaQuery("keywords").contains("java");
Flux<Conference> result = operations.find(query, Conference.class);interface ConferenceRepository extends ReactiveCrudRepository<Conference, String> {
Flux<Conference> findAllByKeywordsContains(String keyword);
}
// ...
@Autowired ConferenceRepository repository;
// ...
Flux<Conference> result = repository.findAllByKeywordsContains("java");Requirements:
Running Elasticsearch
$ cd elasticsearch
$ ./bin/elasticsearchOverview (`jpa/jpa21/README.md`)
Spring Data JPA - JPA 2.1 example
This project contains samples of JPA 2.1 specific features of Spring Data JPA.
Support for declarative Fetch Graphs customization
You can customize the loading of entity associations via EntityGraphs. JPA 2.1 provides the NamedEntityGraph annotation
that allows you to define fetching behavior in a flexible way.
In Spring Data JPA we support to specify which fetch-graph to use for a repository query method via the EntityGraph annotation.
You can refer to a fetch graph by name like in the following example.
@EntityGraph("product-with-tags")
Product findOneById(Long id);We also offer an alternative and more concise way to declaratively specify a fetch graph for a repository query method in an
ad-hoc manner:
@EntityGraph(attributePaths = "tags")
Product getOneById(Long id);By explicitly specifying which associations to fetch via the attributePaths attribute you don't need to specify aNamedEntityGraph annotation on your entity :)
Support for stored procedure execution
You can execute stored procedures either predefined using the JPA 2.1 mapping annotations or dynamically let the stored procedure definition be derived from the repository method name.
Stored procedure declaration in the database (schema.sql):
DROP procedure IF EXISTS plus1inout
/;
CREATE procedure plus1inout (IN arg int, OUT res int)
BEGIN ATOMIC
set res = arg + 1;
END
/;JPA 2.1 stored procedure declaration:
@Entity
@NamedStoredProcedureQuery(name = "User.plus1", procedureName = "plus1inout", parameters = {
@StoredProcedureParameter(mode = ParameterMode.IN, name = "arg", type = Integer.class),
@StoredProcedureParameter(mode = ParameterMode.OUT, name = "res", type = Integer.class) })
public class User {
@Id @GeneratedValue//
private Long id;
}Spring Data JPA repository declaration to execute procedures:
public interface UserRepository extends CrudRepository<User, Long> {
// Explicitly mapped to named stored procedure {@code User.plus1} in the {@link EntityManager}.
// By default, we would've tried to find a procedure declaration named User.plus1BackedByOtherNamedStoredProcedure
@Procedure(name = "User.plus1")
Integer plus1BackedByOtherNamedStoredProcedure(@Param("arg") Integer arg);
// Directly map the method to the stored procedure in the database (to avoid the annotation madness on your domain classes).
@Procedure
Integer plus1inout(Integer arg);
}Calling UserRepository.plus1BackedByOtherNamedStoredProcedure(โฆ) will execute the stored procedure plus1inout using the meta-data declared on the User domain class.
UserRepository.plus1inout(โฆ) will derive stored procedure metadata from the repository and default to positional parameter binding and expect a single output parameter of the backing stored procedure.
Support for custom SqlResultSetMapping with ConstructorResult
Sometimes, e.g. for analytics, it is handy to be able to return a different entity result type from a Repository query method than the base Repository entity type or an interface based projection.
In those cases one can leverage JPAs SqlResultSetMapping feature to map the columns of the result of a query to different fields.
JPA 2.1 introduced the new SqlResultSetMapping type ConstructorResult which allows to map columns of a result set row to a constructor invocation
which can be nicely used in combination with Value Objects.
This example shows how to define a custom SqlResultSetMapping for the result of an analytical native query that reports the usage summary for a set of Subscriptions.
SqlResultSetMapping definition on the Subscription entity class:
@Entity
@NoArgsConstructor
@SqlResultSetMapping(
name = "subscriptionSummary",
classes = @ConstructorResult(
targetClass = SubscriptionSummary.class,
columns = {
@ColumnResult(name = "productName", type = String.class),
@ColumnResult(name = "subscriptions", type = long.class)
}))
@NamedNativeQuery(
name = "Subscription.findAllSubscriptionSummaries",
query = "select product_name as productName, count(user_id) as subscriptions from subscription group by product_name order by productName",
resultSetMapping = "subscriptionSummary"
)
@Data
public class Subscription {
โฆ
}SubscriptionSummary is modelled as a value object:
@Value
public class SubscriptionSummary {
String product;
Long usageCount;
}The SubscriptionRepository declares the custom query method findAllSubscriptionSummaries which is backed by the named native query declared on the Subscription entity.
interface SubscriptionRepository extends CrudRepository<Subscription,Long> {
@Query(nativeQuery = true)
List<SubscriptionSummary> findAllSubscriptionSummaries();
}Overview (`jpa/multiple-datasources/README.md`)
Spring Data JPA - Multiple datasources example
This project contains an example of how to set up Spring Data to work with two independent databases. Note, that this example is not about JTA transactions spanning multiple databases but rather just interacting with separate databases through a cleanly separated domain model and Spring Data repositories.
Interesting points to look at
To connect to two databases we need to manually configure a DataSource, EntityManagerFactory and JpaTransactionManager. This is done in CustomerConfig and OrderConfig. DataInitializer then simulates a Spring component interacting with those databases in separate methods. Application.init() orchestrates calls to those methods.
Overview (`jpa/query-by-example/README.md`)
Spring Data JPA - Query-by-Example (QBE) example
This project contains samples of Query-by-Example of Spring Data JPA.
Support for Query-by-Example
Query by Example (QBE) is a user-friendly querying technique with a simple interface. It
allows dynamic query creation and does not require to write queries containing field
names. In fact, Query by Example does not require to write queries using JPA-QL at all.
An Example takes a data object (usually the entity object or a subtype of it) and a
specification how to match properties. You can use Query by Example with JPA Repositories.
public interface PersonRepository extends CrudRepository<Person, String>, QueryByExampleExecutor<Person> {
}Example<Person> example = Example.of(new Person("Jon", "Snow"));
repo.findAll(example);
ExampleMatcher matcher = ExampleMatcher.matching().
.withMatcher("firstname", endsWith())
.withMatcher("lastname", startsWith().ignoreCase());
Example<Person> example = Example.of(new Person("Jon", "Snow"), matcher);
repo.count(example);This example contains a test class to illustrate Query-by-Example with a Repository
in UserRepositoryIntegrationTests.
Overview (`jpa/vector-search/README.md`)
Spring Data JPA - Vector Search Example
This project contains Vector Search with Spring Data JPA and the hibernate-vector module.
Vector Support
The Spring Data Vector type can be used in repository query methods.
Domain type properties of managed domain types are required to use a numeric array representation for embeddings.
@Entity
@Table(name = "jpa_comment")
public class Comment {
@Id
@GeneratedValue private Long id;
private String country;
private String description;
@JdbcTypeCode(SqlTypes.VECTOR)
@Array(length = 5)
private float[] embedding;
// ...
}
public interface CommentRepository extends Repository<Comment, String> {
SearchResults<Comment> searchTop10ByCountryAndEmbeddingNear(String country, Vector vector, Score distance);
}This example contains a test class to illustrate vector search with a Repository in JpaVectorSearchTest.
Overview (`ldap/example/README.md`)
Spring Data LDAP - Example
This project contains samples of Spring Data (LDAP).
public interface PersonRepository extends CrudRepository<Person, Name> {
List<Person> findByUid(String uid);
List<Person> findByLastnameStartsWith(String prefix);
}The test cases in PersonRepositoryIntegrationTests show basic interaction to search, create and modify objects stored in an LDAP repository.
Overview (`mongodb/aggregation/README.md`)
Spring Data MongoDB - Aggregations
This project contains samples for using MongoDB Aggregations
showing both the programmatic and declarative approach for integrating an Aggregation Pipeline into a repository.
Programmatic
The programmatic approach uses a custom repository implementation along with the Aggregation Framework.
class OrderRepositoryImpl implements OrderRepositoryCustom {
private MongoOperations operations;
// ...
@Override
public Invoice getInvoiceFor(Order order) {
AggregationResults<Invoice> results = operations.aggregate(newAggregation(Order.class,
match(where("id").is(order.getId())),
unwind("items"),
project("id", "customerId", "items")
.andExpression("'$items.price' * '$items.quantity'").as("lineTotal"),
group("id")
.sum("lineTotal").as("netAmount")
.addToSet("items").as("items"),
project("id", "items", "netAmount")
.and("orderId").previousOperation()
.andExpression("netAmount * [0]", taxRate).as("taxAmount")
.andExpression("netAmount * (1 + [0])", taxRate).as("totalAmount")
), Invoice.class);
return results.getUniqueMappedResult();
}
}Declarative
The declarative approach allows to define an Aggregation Pipeline via the @Aggregation annotation.
public interface OrderRepository extends CrudRepository<Order, String>, OrderRepositoryCustom {
@Aggregation("{ $group : { _id : $customerId, total : { $sum : 1 } } }")
List<OrdersPerCustomer> totalOrdersPerCustomer(Sort sort);
@Aggregation(pipeline = { "{ $match : { customerId : ?0 } }", "{ $count : total }" })
Long totalOrdersForCustomer(String customerId);
}Overview (`mongodb/change-streams/README.md`)
Spring Data MongoDB - Change Streams Example
This project contains usage samples for consuming MongoDB 3.6 Change Streams using the imperative as well as the reactive MongoDB Java drivers.
Imperative Style
Change stream events can be consumed using a MessageListener registered within a MessageListenerContainer. The container takes care of running the task in a separate Thread pushing events to the MessageListener.
@Configuration
class Config {
@Bean
MessageListenerContainer messageListenerContainer(MongoTemplate template) {
return new DefaultMessageListenerContainer(template);
}
}Once the MessageListenerContainer is in place MessageListeners can be registered.
MessageListener<ChangeStreamDocument<Document>, Person> messageListener = (message) -> {
System.out.println("Hello " + message.getBody().getFirstname());
};
ChangeStreamRequest<Person> request = ChangeStreamRequest.builder()
.collection("person")
.filter(newAggregation(match(where("operationType").is("insert"))))
.publishTo(messageListener)
.build();
Subscription subscription = messageListenerContainer.register(request, Person.class);
// ...Reactive Style
Change stream events be directly consumed via a Flux connected to the change stream.
Flux changeStream = reactiveTemplate
.changeStream(newAggregation(match(where("operationType").is("insert"))),
Person.class, ChangeStreamOptions.empty(), "person");
changeStream.doOnNext(event -> System.out.println("Hello " + event.getBody().getFirstname()))
.subscribe();Overview (`mongodb/example/README.md`)
Spring Data MongoDB - Example
This project contains examples for:
- Derived query method and geospatial queries using a
Repositoryinterface. - Usage of property-specific converters.
EntityCallbackAPI usage for before convert/save interaction.- Result projections for DTOs and interface types.
- Bulk write to multiple collections.
- Query metadata.
- Unwrapping entities into the parent document.
Overview (`mongodb/fluent-api/README.md`)
Spring Data MongoDB 2.0 - FluentMongoOperations Example
This project contains usage samples of FluentMongoOperations.
Fluent API
FluentMongoOperations provides a stripped down, more focused API alternative for classic MongoOperations.
The main entry points are typical tasks for finding and manipulating Documents on a domain type base, while operations like creating indexes are left out.
For convenience classic MongoOperations extend FluentMongoOperations, however for most cases it might be sufficient to just work with the reduced interface.
To get started just inject FluentMongoOperations.
The entry point methods of FluentMongoOperations provide you with an immutable fluent API allowing only valid next steps while constructing the operation to execute. This allows to set up operations once and keep those in memory for multiple executions.
Query
Looking at the following query(SWCharacter.class).
SWCharacter is used for mapping the query properties to the actual document field names. As the SWCharacter defines @Field("firstname") name; the where clause of the query is mapped to the MongoDB representation as { firstname : "luke" }.
Using as(Jedi.class) switches return type mapping from SWCharacter to Jedi which allows to map resulting documents to a different type than the one used for query mapping.
So far no actual query execution has been invoked. Calling on of the terminating methods like one(), first(), all(),... triggers the query.
mongoOps.query(SWCharacter.class)
.inCollection("star-wars")
.as(Jedi.class)
.matching(query(where("name").is("luke")))
.one();Different stages in the command assembly process allow to seamlessly switch to different API paths. Using near instead of matching switches to the path for geo queries requiring the presence of a NearQuery while altering the command result type from List to GeoResults and limiting terminating operations to just all().
NearQuery alderaanWithin3Parsecs = NearQuery.near(-73.9667, 40.78)
.maxDistance(new Distance(3, MILES))
.spherical(true);
GeoResults<Jedi> results = mongoOps.query(SWCharacter.class)
.as(Jedi.class)
.near(alderaanWithin3Parsecs)
.all();Update
Looking at the following update(Jedi.class).
Jedi already defines the collection via the @Document annotation, so there is no need to explicitly specify a collection name via inCollection(String). The Jedi domain type is also used for query and update mapping.
So far no actual query execution has been invoked. Calling on of the terminating methods like all(), upsert(), findAndModify(), etc. triggers the update.
mongoOps.update(Jedi.class)
.matching(query(where("lastname").is("windu")))
.apply(update("name", "mence"))
.upsert();Overview (`mongodb/geo-json/README.md`)
Spring Data MongoDB - GeoJSON examples
This project contains samples of GeoJSON specific features of Spring Data (MongoDB).
Support for GeoJSON types in domain classes
Using GeoJSON types in domain classes is straight forward. The org.springframework.data.mongodb.core.geo package contains types like GeoJsonPoint or GeoJsonPolygon which are extensions to the existing org.springframework.data.geo types.
Find more information in the MongoDB manual on GeoJSON support to learn about requirements and restrictions.
public class Store {
String id;
/**
* location is stored in GeoJSON format.
* {
* "type" : "Point",
* "coordinates" : [ x, y ]
* }
*/
GeoJsonPoint location;
}Support for GeoJSON types in repository methods
Using GeoJson types as repository query parameters forces usage of the $geometry operator when creating the query.
public interface StoreRepository extends CrudRepository<Store, String> {
List<Store> findByLocationWithin(Polygon polygon);
}/* {
* "location": {
* "$geoWithin": {
* "$geometry": {
* "type": "Polygon",
* "coordinates": [
* [
* [-73.992514,40.758934],
* [-73.961138,40.760348],
* [-73.991658,40.730006],
* [-73.992514,40.758934]
* ]
* ]
* }
* }
* }
* }
*/
repo.findByLocationWithin(
new GeoJsonPolygon(
new Point(-73.992514, 40.758934),
new Point(-73.961138, 40.760348),
new Point(-73.991658, 40.730006),
new Point(-73.992514, 40.758934)));
/* {
* "location" : {
* "$geoWithin" : {
* "$polygon" : [ [ -73.992514, 40.758934 ] , [ -73.961138, 40.760348 ] , [ -73.991658, 40.730006 ] ]
* }
* }
* }
*/
repo.findByLocationWithin(
new Polygon(
new Point(-73.992514, 40.758934),
new Point(-73.961138, 40.760348),
new Point(-73.991658, 40.730006));Overview (`mongodb/gridfs/README.md`)
Spring Data MongoDB - GridFS example
This project contains an example of GridFS specific features of Spring Data (MongoDB).
Support for storing a file
Using GridFsOperations to store a file.
InputStream is = ...
gridFsOperations.store(is, "myFile1.txt");Support for query a file by name
Using GridFsOperations to query a GridFSDBFile by its filename.
GridFSDBFile gridFsFile = gridFsOperations.findOne(query(whereFilename().is("myFile1.txt")));Support for storing a file with metadata
Using GridFsOperations to store a file with additional metadata..
InputStream is = ...
Customer customerMetaData = new Customer("Hardy", "Lang");
gridFsOperations.store(is, "myCustomerFile.txt", customerMetaData);Support for query a file by metadata
Using GridFsOperations to query a GridFSDBFile by metadata.
GridFSDBFile gridFsFile = gridFsOperations.findOne(query(whereMetaData("firstName").is("Hardy")));Overview (`mongodb/kotlin/README.md`)
Spring Data MongoDB - Kotlin examples
This project contains samples of Kotlin-specific features of Spring Data (MongoDB).
Value defaulting on entity construction
Kotlin allows defaulting for constructor- and method arguments.
Defaulting allows usage of substitute values if a field in the document is absent or simply null.
Spring Data inspects objects whether they are Kotlin types and uses the appropriate constructor.
data class Person (@Id val id: String?, val firstname: String? = "Walter", val lastname: String)
operations.insert<Document>().inCollection("person").one(Document("lastname", "White"))
val walter = operations.findOne<Document>(query(where("lastname").isEqualTo("White")), "person")
assertThat(walter.firstname).isEqualTo("Walter")Kotlin Extensions
Spring Data exposes methods accepting a target type to either query for or to project results values on.
Kotlin represents classes with its own type, KClass which can be an obstacle when attempting to obtain a Java Class type.
Spring Data ships with extensions that add overloads for methods accepting a type parameter by either leveraging generics or accepting KClass directly.
operations.getCollectionName<Person>()
operations.getCollectionName(Person::class)Nullability
Declaring repository interfaces using Kotlin allows expressing nullability constraints on arguments and return types. Spring Data evaluates nullability of arguments and return types and reacts to these. Passing null to a non-nullable argument raises an IllegalArgumentException, as you're already used to from Kotlin. Spring Data helps you also to prevent null in query results. If you wish to return a nullable result, use Kotlin's nullability marker ?. To prevent null results, declare the return type of a query method as non-nullable. In the case a query yields no result, a non-nullable query method throws EmptyResultDataAccessException.
interface PersonRepository : CrudRepository<Person, String> {
/**
* Query method declaring a nullable return type that allows to return null values.
*/
fun findOneOrNoneByFirstname(firstname: String): Person?
/**
* Query method declaring a nullable argument.
*/
fun findNullableByFirstname(firstname: String?): Person?
/**
* Query method requiring a result. Throws [org.springframework.dao.EmptyResultDataAccessException] if no result is found.
*/
fun findOneByFirstname(firstname: String): Person
}Type-Safe Kotlin Mongo Query DSL
Using the Criteria extensions allows to write type-safe queries via an idiomatic API.
operations.find<Person>(Query(Person::firstname isEqualTo "Tyrion"))Coroutines and Flow support
runBlocking {
operations.find<Person>(Query(where("firstname").isEqualTo("Tyrion"))).awaitSingle()
}runBlocking {
operations.findAll<Person>().asFlow().toList()
}Overview (`mongodb/linking/README.md`)
Spring Data MongoDB - Linking Example
This project contains examples for linking Documents using:
@DBRef@DocumentReference
DBRef
Use MongoDB native dbref for storing associations.
class Manager {
@DBRef
private List<Employee> employees;
// ...
}DocumentReference
Link documents via their id.
class Manager {
@DocumentReference
private List<Employee> employees;
// ...
}Link documents via a (non id) property.
class Manager {
@DocumentReference(lookup = "{ 'name' : ?#{#target} }")
private List<Employee> employees;
// ...
}Link documents JPA style.
class Manager {
@ReadOnlyProperty
@DocumentReference(lookup="{'managerId':?#{#self._id} }")
private List<Employee> employees;
// ...
}For more information please refer to the Reference Documentation.
Overview (`mongodb/query-by-example/README.md`)
Spring Data MongoDB - Query-by-Example (QBE) example
This project contains samples of Query-by-Example of Spring Data MongoDB.
Support for Query-by-Example
Query by Example (QBE) is a user-friendly querying technique with a simple interface. It
allows dynamic query creation and does not require to write queries containing field
names. In fact, Query by Example does not require to write queries using BSON at all.
An Example takes a data object (usually the entity object or a subtype of it) and a
specification how to match properties. You can use Query by Example with MongoOperations
and Repositories.
interface PersonRepository extends CrudRepository<Person, String>, QueryByExampleExecutor<Person> {
}Example<Person> example = Example.of(new Person("Jon", "Snow"));
repo.findAll(example);
ExampleMatcher matcher = ExampleMatcher.matching().
.withMatcher("firstname", endsWith())
.withMatcher("lastname", startsWith().ignoreCase());
Example<Person> example = Example.of(new Person("Jon", "Snow"), matcher);
repo.count(example);This example contains two test classes to illustrate Query-by-Example with MongoOperations in MongoOperationsIntegrationTests and the usage with a Repository in UserRepositoryIntegrationTests.
Overview (`mongodb/querydsl/README.md`)
Spring Data MongoDB - Querydsl example
This project contains samples of Querydsl usage in Spring Data MongoDB.
Querydsl is a framework which enables the construction of fluent, type-safe queries for multiple backends including MongoDB.
Spring Data integrates with Querydsl via QuerydslPredicateExecutor and its reactive counterpart ReactiveQuerydslPredicateExecutor.
NOTE: You may have to run mvn compile to generate the required Q classes first.
Imperative
interface CustomerQuerydslRepository
extends CrudRepository<Customer, String>, QuerydslPredicateExecutor<Customer> { }
@Autowired CustomerQuerydslRepository repository;
// ...
List<Customer> result = repository.findAll(QCustomer.customer.lastname.eq("Matthews"));Reactive
interface ReactiveCustomerQuerydslRepository
extends ReactiveCrudRepository<Customer, String>, ReactiveQuerydslPredicateExecutor<Customer> { }
@Autowired ReactiveCustomerQuerydslRepository repository;
// ...
Flux<Customer> result = repository.findAll(QCustomer.customer.lastname.eq("Matthews"));Overview (`mongodb/reactive/README.md`)
Spring Data MongoDB 2.0 - Reactive examples
This project contains samples of reactive data access features with Spring Data (MongoDB).
Prerequisites
MongoDB requires the Reactive Streams driver to provide reactive data access.
The Reactive Streams driver maintains its own connections. Using Spring Data MongoDB Reactive support
together with blocking Spring Data MongoDB data access will open multiple connections to your MongoDB servers.
Reactive Template API usage with `ReactiveMongoTemplate`
The main reactive Template API class is ReactiveMongoTemplate, ideally used through its interface ReactiveMongoOperations. It defines a basic set of reactive data access operations using Project Reactor Mono and Flux reactive types.
template.insertAll(Arrays.asList(new Person("Walter", "White", 50),
new Person("Skyler", "White", 45),
new Person("Saul", "Goodman", 42),
new Person("Jesse", "Pinkman", 27)));
Flux<Person> flux = template.find(Query.query(Criteria.where("lastname").is("White")), Person.class);The test cases in ReactiveMongoTemplateIntegrationTest show basic Template API usage.
Reactive data access reads and converts individual elements while processing the stream.
Reactive Repository support
Spring Data MongoDB provides reactive repository support with Project Reactor and RxJava 2 reactive types. The reactive API supports reactive type conversion between reactive types.
public interface ReactivePersonRepository extends ReactiveCrudRepository<Person, String> {
Flux<Person> findByLastname(String lastname);
@Query("{ 'firstname': ?0, 'lastname': ?1}")
Mono<Person> findByFirstnameAndLastname(String firstname, String lastname);
// Accept parameter inside a reactive type for deferred execution
Flux<Person> findByLastname(Mono<String> lastname);
Mono<Person> findByFirstnameAndLastname(Mono<String> firstname, String lastname);
@Tailable // Use a tailable cursor
Flux<Person> findWithTailableCursorBy();
}public interface RxJava2PersonRepository extends RxJava2CrudRepository<Person, String> {
Flowable<Person> findByLastname(String lastname);
@Query("{ 'firstname': ?0, 'lastname': ?1}")
Maybe<Person> findByFirstnameAndLastname(String firstname, String lastname);
// Accept parameter inside a reactive type for deferred execution
Flowable<Person> findByLastname(Single<String> lastname);
Maybe<Person> findByFirstnameAndLastname(Single<String> firstname, String lastname);
@Tailable // Use a tailable cursor
Flowable<Person> findWithTailableCursorBy();
}Overview (`mongodb/repository-metrics/README.md`)
Spring Data MongoDB 3.1 - Repository Metrics
Configure a RepositoryMethodInvocationListener to capture invocation metrics on Repository interfaces.
@Configuration(proxyBeanMethods = false)
class RepositoryMetricsConfiguration {
@Bean
public RepoMetricsPostProcessor repoMetricsPostProcessor() {
return new RepoMetricsPostProcessor();
}
static class RepoMetricsPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof RepositoryFactoryBeanSupport<?, ?, ?> repositoryFactoryBean) {
repositoryFactoryBean
.addRepositoryFactoryCustomizer(repositoryFactory -> {
repositoryFactory
.addInvocationListener(System.out::println); // register the invocation listener
});
}
return bean;
}
}
}Overview (`mongodb/schema-validation/README.md`)
Spring Data MongoDB 2.1 - Schema & Validation Example
MongoDB (as of version 3.2) supports validating documents against a given structure described by a query.
{
"name" : {
"$exists" : true,
"$ne" : null,
"$type" : 2
},
"age" : {
"$exists" : true,
"$ne" : null,
"$type" : 16,
"$gte" : 0,
"$lte" : 125
}
}The structure can be built from Criteria objects in the same way as they are used for defining queries.
Validator.criteria(where("name").exists(true).ne(null).type(2)
.and("age").exists(true).ne(null).type(16).gte(0).lte(125));MongoDB 3.6 supports collections that validate documents against a provided JSON Schema that
complies with the JSON schema specification (draft 4).
{
"type": "object",
"required": [ "name", "age" ],
"properties": {
"name": {
"type": "string",
"minLength": 1
},
"age": {
"type": "int",
"minimum" : 0,
"exclusiveMinimum" : false,
"maximum" : 125,
"exclusiveMaximum" : false
}
}
}The MongoJsonSchema and its builder allow fluent schema definition via the Java API.
MongoJsonSchema schema = MongoJsonSchema.builder() //
.required("name", "age") //
.properties( //
string("name").minLength(1), //
int32("age").gte(0).lte(125) //
).build();The schema can be used for various functionality: Set up Document validation for a collection:
template.createCollection(Jedi.class, CollectionOptions.empty().validator(Validator.schema(schema)));and to query the store for documents matching a given blueprint:
template.find(query(matchingDocumentStructure(schema)), Jedi.class);Overview (`mongodb/text-search/README.md`)
Spring Data MongoDB - Text Search Examples
This project contains samples of text search specific features of Spring Data Mongodb.
Support for Text Index
Define text index structures manually (like below) or use @TextIndexed to mark content to be indexed for full text search.
TextIndexDefinition textIndex = new TextIndexDefinitionBuilder()
.onField("title", 3F)
.onField("content", 2F)
.onField("categories")
.build();
template.indexOps(BlogPost.class).ensureIndex(textIndex);Support for full text repository queries
Use derived finder methods to search for terms and phrases.
interface BlogPostRepository extends CrudRepository<BlogPost, String> {
// page through results for full text query
Page<BlogPost> findBy(TextCriteria criteria, Pageable page);
// find all matching documents and sort by relevance
List<BlogPost> findAllByOrderByScoreDesc(TextCriteria criteria);
}Overview (`mongodb/transactions/README.md`)
Spring Data MongoDB - Transaction Sample
This project contains samples for upcoming MongoDB 4.0 transactions.
Running the Sample
The sample uses multiple embedded MongoDB processes in a MongoDB replica set.
It contains test for both the synchronous and the reactive transaction support in the sync / reactive packages.
You may run the examples directly from your IDE or use maven on the command line.
INFO: The operations to download the required MongoDB binaries and spin up the cluster can take some time. Please
be patient.
Sync Transactions
MongoTransactionManager is the gateway to the well known Spring transaction support. It lets applications use
the managed transaction features of Spring.
The MongoTransactionManager binds a ClientSession to the thread. MongoTemplate detects the session and operates
on these resources which are associated with the transaction accordingly. MongoTemplate can also participate in
other, ongoing transactions.
@Configuration
static class Config extends AbstractMongoConfiguration {
@Bean
MongoTransactionManager transactionManager(MongoDbFactory dbFactory) {
return new MongoTransactionManager(dbFactory);
}
// ...
}
@Component
public class TransitionService {
@Transactional
public void run(Integer id) {
Process process = lookup(id);
if (!State.CREATED.equals(process.getState())) {
return;
}
start(process);
verify(process);
finish(process);
}
}Programmatic Reactive transactions
ReactiveMongoTemplate offers dedicated methods (like inTransaction()) for operating within a transaction without having to worry about the
commit/abort actions depending on the operations outcome.
NOTE: Please note that you cannot preform meta operations, like collection creation within a transaction.
@Service
public class ReactiveTransitionService {
public Mono<Integer> run(Integer id) {
return template.inTransaction().execute(action -> {
return lookup(id) //
.filter(State.CREATED::equals)
.flatMap(process -> start(action, process))
.flatMap(this::verify)
.flatMap(process -> finish(action, process));
}).next().map(Process::getId);
}
}Declarative Reactive transactions
ReactiveMongoTransactionManager is the gateway to the reactive Spring transaction support. It lets applications use
the managed transaction features of Spring.
The ReactiveMongoTransactionManager adds the ClientSession to the reactor.util.context.Context. ReactiveMongoTemplate detects the session and operates
on these resources which are associated with the transaction accordingly.
@EnableTransactionManagement
class Config extends AbstractReactiveMongoConfiguration {
@Bean
ReactiveTransactionManager transactionManager(ReactiveMongoDatabaseFactory factory) {
return new ReactiveMongoTransactionManager(factory);
}
// ...
}
@Service
class ReactiveManagedTransitionService {
@Transactional
public Mono<Integer> run(Integer id) {
return lookup(id)
.flatMap(process -> start(template, process))
.flatMap(it -> verify(it)) //
.flatMap(process -> finish(template, process))
.map(Process::getId);
}
}Overview (`mongodb/vector-search/README.md`)
Spring Data MongoDB - Vector Search Example
This project
contains Vector Search
with Spring Data MongoDB.
Vector Support
The Spring Data Vector type can be used in repository query methods.
Domain type properties of managed domain types are required to use a numeric array representation for embeddings.
@Document
public class Comment {
@Id
private ObjectId id;
private String country;
private String description;
private Vector embedding;
// ...
}
public interface CommentRepository extends Repository<Comment, String> {
@VectorSearch(indexName = "cosine-index", searchType = VectorSearchOperation.SearchType.ANN)
SearchResults<Comment> searchTop10ByCountryAndEmbeddingNear(String country, Vector vector, Score distance);
}This example contains a test class to illustrate vector search with a Repository in MongoDBVectorSearchTest.
Overview (`redis/cluster-sentinel/README.md`)
Spring Data Redis Example
This example was named a bit misleadingly as cluster-sentinel, a mix of Redis Cluster and Redis Sentinel.
Find dedicated examples here: Redis Cluster and Redis Sentinel.
Overview (`redis/cluster/readme.md`)
Spring Data Redis - Cluster Examples
This project contains Redis 3 Cluster specific features of Spring Data Redis.
To run the code in this sample a running cluster environment is required. Please refer to the redis cluster-tutorial for detailed information or check the Cluster Setup section below.
Support for Cluster
Cluster Support uses the same building blocks as the non-clustered counterpart. We use application.properties to point to an initial set of known cluster nodes which will be picked up by the auto-configuration.
spring.redis.cluster.nodes[0]=127.0.0.1:30001
spring.redis.cluster.nodes[1]=127.0.0.1:30002
spring.redis.cluster.nodes[2]=127.0.0.1:30003INFORMATION: The tests flush the db of all known instances during the JUnit setup phase to allow inspecting data directly on the cluster nodes after a test is run.
Cluster Setup
To quickly set up a cluster of 6 nodes (3 master | 3 slave) go to the redis/utils/create-cluster directory.
redis/utils/create-cluster $ ./create-cluster start
Starting 30001
Starting 30002
Starting 30003
Starting 30004
Starting 30005
Starting 30006On first initialization cluster nodes need to form the cluster by joining and assigning slot allocations.
INFO: This has to be done only once.
redis/utils/create-cluster $ ./create-cluster create
>>> Creating cluster
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
127.0.0.1:30001
127.0.0.1:30002
127.0.0.1:30003
Adding replica 127.0.0.1:30004 to 127.0.0.1:30001
Adding replica 127.0.0.1:30005 to 127.0.0.1:30002
Adding replica 127.0.0.1:30006 to 127.0.0.1:30003
M: 10696916f57e58c5edce34127b23ca7af1b669a0 127.0.0.1:30001
slots:0-5460 (5461 slots) master
M: 5b0e1b4cc87175326ba79d00ecfc6f5dbdb424a7 127.0.0.1:30002
slots:5461-10922 (5462 slots) master
M: 5f3e978fb40b1d9c910d904ea19a0494b78668aa 127.0.0.1:30003
slots:10923-16383 (5461 slots) master
S: d1717c418d03db93183ce2d791ba6f48be5cf028 127.0.0.1:30004
replicates 10696916f57e58c5edce34127b23ca7af1b669a0
S: c7dfcdb9cd1105e4251de51c4ade54de59bb063c 127.0.0.1:30005
replicates 5b0e1b4cc87175326ba79d00ecfc6f5dbdb424a7
S: 3219785a9145717f30648a27a2dd07359e9dd46f 127.0.0.1:30006
replicates 5f3e978fb40b1d9c910d904ea19a0494b78668aa
Can I set the above configuration? (type 'yes' to accept): yes
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.It is now possible to connect to the cluster using the redis-cli.
redis/src $ ./redis-cli -c -p 30001
127.0.0.1:30001> cluster nodes
106969... 127.0.0.1:30001 myself,master - 0 0 1 connected 0-5460
5b0e1b... 127.0.0.1:30002 master - 0 1450765112345 2 connected 5461-10922
5f3e97... 127.0.0.1:30003 master - 0 1450765112345 3 connected 10923-16383
d1717c... 127.0.0.1:30004 slave 106969... 0 1450765112345 4 connected
c7dfcd... 127.0.0.1:30005 slave 5b0e1b... 0 1450765113050 5 connected
321978... 127.0.0.1:30006 slave 5f3e97... 0 1450765113050 6 connectedTo shut down the cluster use the create-cluster stop command.
redis/utils/create-cluster $ ./create-cluster stopOverview (`redis/example/README.md`)
Spring Data Redis - Examples
This project contains samples of specific features of Spring Data Redis.
Overview (`redis/pubsub-listener/README.md`)
Spring Data Redis - Annotated Listener Example
This project demonstrates declarative Redis Pub/Sub listeners.
Features
@RedisListeneron component methods to subscribe to Redis channels@EnableRedisListenersto activate the listener infrastructure- Receiving string messages on a channel
- Receiving JSON payloads deserialized to domain types (e.g.
Person)
Requirements
- Docker (for running integration tests with Testcontainers).
Running the tests
Integration tests publish messages to Redis channels and assert that the annotated listeners receive and process them. They use Testcontainers to start a Redis container, so Docker must be running.
cd redis/listener
mvn testOverview (`redis/pubsub/README.md`)
Spring Data Redis Pub/Sub Example
This project contains samples of specific features of Spring Data Redis.
Overview (`redis/reactive/README.md`)
Spring Data Redis 2.0 - Reactive examples
This project contains samples of reactive data access features with Spring Data (Redis).
Prerequisites
This project contains samples of specific features of Spring Data Redis using reactive infrastructure.
Overview (`redis/repositories/README.md`)
Spring Data Redis - Repository Examples
This project contains examples for Spring Data specific repository abstraction on top of Redis.
Repository Support
Redis Repository support allows to convert, store, retrieve and index entities within Redis native data structures. To do, besides the HASH containing the actual properties several Secondary Index structures are set up and maintained.
@RedisHash("persons")
class Person {
@Id String id;
@Indexed String firstname;
@Indexed String lastname;
Gender gender;
Address address;
@Reference List<Person> children;
}The above entity would for example then be stored in a Redis HASH with key persons:9b0ed8ee-14be-46ec-b5fa-79570aadb91d.
_class=example.springdata.redis.domain.Person <1>
id=9b0ed8ee-14be-46ec-b5fa-79570aadb91d
firstname=eddard <2>
lastname=stark
gender=MALE
address.city=winterfell <3>
address.country=the north
children.[0]=persons:41436096-aabe-42fa-bd5a-9a517fbf0260 <4>
children.[1]=persons:1973d8e7-fbd4-4f93-abab-a2e3a00b3f53
children.[2]=persons:440b24c6-ede2-495a-b765-2d8b8d6e3995<1> The _class attribute is used to store the actual type and is required for object/hash conversion.
<2> Values are also included in Secondary Index when annotated with @Indexed.
<3> Complex types are flattened out and embedded into the HASH as long as there is no explicit Converter registered or a @Reference annotation present.
<4> Using @Reference stores only the key of a referenced object without embedding values like in <3>.Additionally indexes are created for firstname, lastname and address.city containing the id of the actual entity.
redis/src $ ./redis-cli keys *
1) "persons" <1>
2) "persons:9b0ed8ee-14be-46ec-b5fa-79570aadb91d" <2>
3) "persons:9b0ed8ee-14be-46ec-b5fa-79570aadb91d:idx" <3>
4) "persons:41436096-aabe-42fa-bd5a-9a517fbf0260"
5) "persons:41436096-aabe-42fa-bd5a-9a517fbf0260:idx"
6) "persons:1973d8e7-fbd4-4f93-abab-a2e3a00b3f53"
7) "persons:1973d8e7-fbd4-4f93-abab-a2e3a00b3f53:idx"
8) "persons:440b24c6-ede2-495a-b765-2d8b8d6e3995"
9) "persons:440b24c6-ede2-495a-b765-2d8b8d6e3995:idx"
10) "persons:firstname:eddard" <4>
11) "persons:firstname:robb"
12) "persons:firstname:sansa"
13) "persons:firstname:arya"
14) "persons:lastname:stark" <5>
15) "persons:address.city:winterfell" <6><1> SET holding all ids known in the keyspace "persons".
<2> HASH holding property values for id "9b0ed8ee-14be-46ec-b5fa-79570aadb91d" in keyspace "persons".
<3> SET holding index information for CRUD operations.
<4> SET used for indexing entities with "firstname" equal to "eddard" within keyspace "persons".
<5> SET used for indexing entities with "lastname" equal to "stark" within keyspace "persons".
<6> SET used for indexing entities with "address.city" equal to "winterfell" within keyspace "persons".Configuration
The below configuration uses Lettuce to connect to Redis on its default port. Please note the usage of @EnableRedisRepositories to create Repository instances.
@Configuration
@EnableRedisRepositories
class AppConfig {
@Bean
RedisConnectionFactory connectionFactory() {
return new LettuceConnectionFactory();
}
@Bean
RedisTemplate<?, ?> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<byte[], byte[]> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
return template;
}
}Having the infrastructure in place you can go on declaring and using the Repository interface.
interface PersonRepository extends CrudRepository<Person, String> {
List<Person> findByLastname(String lastname);
Page<Person> findByLastname(String lastname, Pageable page);
List<Person> findByFirstnameAndLastname(String firstname, String lastname);
List<Person> findByFirstnameOrLastname(String firstname, String lastname);
List<Person> findByAddress_City(String city);
}One Word of Caution
Maintaining complex types and index structures stands and falls with its usage. Please consider the following:
- Nested document structures increase object <> hash conversion overhead.
- Consider having only those indexes you really need instead of indexing each and every property.
- Pagination is a costly operation since the total number of elements is calculated before returning just a slice of data.
- Pagination gives no guarantee on information as elements might be added, moved or removed.
Overview (`redis/sentinel/README.md`)
Spring Data Redis - Sentinel Examples
This project contains samples of Sentinel specific features of Spring Data Redis.
Support for Sentinel
@Configuration
public class RedisSentinelApplicationConfig {
static final RedisSentinelConfiguration SENTINEL_CONFIG = new RedisSentinelConfiguration().master("mymaster") //
.sentinel("localhost", 26379) //
.sentinel("localhost", 26380) //
.sentinel("localhost", 26381);
@Bean
public RedisConnectionFactory connectionFactory() {
return new LettuceConnectionFactory(sentinelConfig(), LettuceClientConfiguration.defaultConfiguration());
}
@Bean
public RedisSentinelConfiguration sentinelConfig() {
return SENTINEL_CONFIG;
}
}Overview (`redis/streams/README.md`)
Spring Data Redis - Streams Examples
The Redis Stream is a new data type introduced with Redis 5.0 modelling log data structure.
Spring Data Redis supports Redis Streams via both the imperative and the reactive API.
Imperative API
Basic Usage
@Autowired
RedisTemplate template;
StringRecord record = StreamRecords.string(โฆ)
.withStreamKey("my-stream");
RecordId id = template.streamOps().add(record);
List<...> records = template.streamOps().read(count(2), from(id));ContinuousRead Read
@Autowired
RedisConnectionFactory factory;
StreamListener<String, MapRecord<โฆ> listener =
(msg) -> {
// ...
};
StreamMessageListenerContainer container = StreamMessageListenerContainer.create(factory));
container.receive(StreamOffset.fromStart("my-stream"), listener);Reactive API
Basic Usage
@Autowired
ReactiveRedisTemplate template;
StringRecord record = StreamRecords.string(โฆ)
.withStreamKey("my-stream");
Mono<RecordId> id = template.streamOps().add(record);
Flux<...> records = template.streamOps().read(count(2), from(id));ContinuousRead Read
@Autowired
ReactiveRedisConnectionFactory factory;
StreamReceiver receiver = StreamReceiver.create(factory));
container.receive(StreamOffset.fromStart("my-stream"))
.doOnNext((msg) -> {
// ...
})
.subscribe();Overview (`rest/starbucks/README.md`)
Spring Data REST - Starbucks example
This sample app exposes 10843 Starbucks coffee shops via a RESTful API that allows to access the stores in a hypermedia based way and exposes a resource to execute geolocation search for coffee shops.
Quickstart
- Install MongoDB (http://www.mongodb.org/downloads, unzip, run
mkdir data, runbin/mongod --dbpath=data) - Build and run the app (
mvn spring-boot:run) - Access the root resource (
curl http://localhost:8080/api) and traverse hyperlinks. - Or access the location search directly (e.g.
http://localhost:8080/api/stores/search/findByAddressLocationNear?location=40.740337,-73.995146&distance=0.5miles)
Web UI
The application provides a custom web UI using the exposed REST API to display the search result on a Google Map. Point you browser to http://localhost:8080. The UI is rendered using Thymeleaf, driven by the StoresController. A tiny JavaScript progressively enhances the view by picking up and enhancing a URI template rendered into the view (``).
The API itself can be discovered using the HAL browser pulled in through the corresponding Spring Data REST module (spring-data-rest-hal-browser). It's exposed at the API root at http://localhost:8080/api.
Technologies used
- Spring Data REST & Spring Data MongoDB
- MongoDB
- Spring Batch (to read the CSV file containing the store data and pipe it into MongoDB)
- Spring Boot
Overview (`web/example/README.md`)
Spring Data - web support example
This example shows some of the Spring Data integration features with Spring MVC.
- See how we plug into Spring MVC to create
Pageableinstances from request parameters inUserController.users(โฆ). - See how interfaces can be used to bind request payloads in
UserController.UserForm. Spring Data creates aMap-backed proxy for you to easily create form-backing objects.
Overview (`web/projection/README.md`)
Spring Data Web - Projection example
This example shows how to use projection interfaces in combination with JSON Path or XPath expressions to bind payload data to an object instance.
The most interesting bit is the following projection interface declared in UserController:
@ProjectedPayload
public interface UserPayload {
@XBRead("//firstname")
@JsonPath("$..firstname")
String getFirstname();
@XBRead("//lastname")
@JsonPath("$..lastname")
String getLastname();
}This type is used in UserController.index(โฆ) and basically combines two modes of operation.
Binding request data via JSON Path expression
The @JsonPath annotations bind the values obtained by evaluating the expressions from the request. The sample is using a recursive property lookup for firstname and lastname. Using those expressions allows the payload that received to slightly changed and the code dealing with not having to be changed.
As an example using that on the server side can be found in UserControllerIntegrationTests. The tests send two different flavors of JSON to simulate a change in behavior of the client and the server can handle both representation formats without the need for a change.
This is also very useful on the client side which is simulated in UserControllerClientTests setting up a RestTemplate with the newly introduced HttpMessageConverters so that the projection interface can be used to access the payload. See how the test case accesses different HTTP resources, that simulate a change in the representation on the server side.
Support for XPath expressions
The JSON Path support is automatically activated on the server side if the Jayway JSON Path library is on the classpath. Support for XPath is activated if XMLBeam is on the classpath, generally works the same and actually offers quite some more sophisticated features.
Overview (`web/querydsl/README.md`)
Spring Data Web - Querydsl example
This example shows some of the Spring Data Querydsl integration features with Spring MVC.
Quickstart
- Build and run the app (
mvn spring-boot:test-run) - Access app directly via its UI (
http://localhost:8080/).
Interesting bits
The most core piece of interest here is UserController and how it consumes Querydsl Predicate instances derived from the request:
@Controller
@RequiredArgsConstructor(onConstructor = @__(@Autowired) )
class UserController {
private final UserRepository repository;
@RequestMapping(value = "/", method = RequestMethod.GET)
String index(Model model,
@QuerydslPredicate(root = User.class) Predicate predicate, Pageable pageable,
@RequestParam MultiValueMap<String, String> parameters) {
โฆ = userRepository.findAll(predicate, pageable);
}
}As you can see Predicate can be used as Spring MVC controller argument. It will automatically populate such a Predicate with values from the current request based on the type configured in @QuerydslPredicate. Explicit bindings can be configured by either explicitly defining one in the annotation, too. By default, we will inspect the domain types's repository for binding customizations. In this example, it looks like this:
public interface UserRepository
extends CrudRepository<User, String>, QueryDslPredicateExecutor<User>, QuerydslBinderCustomizer<QUser> {
@Override
default public void customize(QuerydslBindings bindings, QUser root) {
bindings.bind(String.class).first((StringPath path, String value) -> path.containsIgnoreCase(value));
bindings.excluding(root.password);
}
}The repository extends QuerydslBinderCustomizer which exposes a QuerydslBindings instance for customization. It allows for property based (by using Querydsl's metamodel types) and type based customizations of the value binding. The example here defines a String-properties to be bound using the containsIgnoreCase(โฆ) operator. For further information checkout the JavaDoc of QuerydslBindings.
Technologies used
- Spring Data MongoDB, Spring MVC & Querydsl
- MongoDB
- Spring Batch (to read the CSV file containing the user data and pipe it into MongoDB)
- Spring Boot
--- METRICS ---
- Files Extracted: 43
- Estimated Token Budget: ~16816 tokens
- Canonical Reference: https://codewiki.google/github.com/spring-projects/spring-data-examples