© 2008-2024 The original authors.

Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.

Preface

1. Project Metadata

Unresolved directive in index.adoc - include::https://raw.githubusercontent.com/spring-projects/spring-data-commons/master/src/main/asciidoc/repositories.adoc[leveloffset=+1]

Reference Documentation

2. Key-Value Repositories

This chapter explains concepts and usage patterns you need to know when working with the key-value abstraction and the java.util.Map based implementation provided by Spring Data Key Value.

2.1. Core Concepts

The key-value abstraction within Spring Data Key Value requires an Adapter that shields the native store implementation, freeing up KeyValueTemplate to work on top of any key-value pair-like structure. Keys are distributed across Keyspaces. Unless otherwise specified, the class name is used as the default keyspace for an entity. The following interface definition shows the KeyValueOperations interface, which is the heart of Spring Data Key-Value:

interface KeyValueOperations {

    <T> T insert(T objectToInsert);                               (1)

    void update(Object objectToUpdate);                           (2)

    void delete(Class<?> type);                                   (3)

    <T> T findById(Object id, Class<T> type);                     (4)

    <T> Iterable<T> findAllOf(Class<T> type);                     (5)

    <T> Iterable<T> find(KeyValueQuery<?> query, Class<T> type);  (6)

    //... more functionality omitted.

}
1 Inserts the given entity and assigns an ID (if required).
2 Updates the given entity.
3 Removes all entities of the matching type.
4 Returns the entity of the given type with its matching ID.
5 Returns all entities of the matching type.
6 Returns a List of all entities of the given type that match the criteria of the query.

2.2. Configuring The KeyValueTemplate

In its very basic shape, the KeyValueTemplate uses a MapAdapter that wraps a ConcurrentHashMap and that uses Spring Expression Language to run queries and sorting.

The used KeyValueAdapter does the heavy lifting when it comes to storing and retrieving data. The data structure influences performance and multi-threading behavior.

You can use a different type or pre-initialize the adapter with some values, and you can do so by using various constructors on MapKeyValueAdapter, as the following example shows:

@EnableMapRepositories
@Configuration
class MyConfiguration {

  @Bean
  public KeyValueOperations mapKeyValueTemplate() {               (1)
    return new KeyValueTemplate(keyValueAdapter());
  }

  @Bean
  public KeyValueAdapter keyValueAdapter() {
    return new MapKeyValueAdapter(ConcurrentHashMap.class);       (2)
  }
}
1 Defines a custom KeyValueOperations bean using the default bean name. See documentation and properties of @EnableMapRepositories for further customization.
2 Defines a custom KeyValueAdapter bean using a ConcurrentHashMap as storage that is used by KeyValueTemplate.

2.3. Keyspaces

Keyspaces define the part of the data structure in which the entity should be kept.This concept is similar to collections in MongoDB and Elasticsearch, cores in Solr, and tables in JPA. By default, the keyspace of an entity is extracted from its type, but you can also store entities of different types within one keyspace.In that case, any find operation type-checks the results.The following example shows a keyspace for a repository of Person objects:

@KeySpace("persons")
class Person {

  @Id String id;
  String firstname;
  String lastname;
}

class User extends Person {
  String username;
}

template.findAllOf(Person.class); (1)
template.findAllOf(User.class);   (2)
1 Returns all entities for the persons keyspace.
2 Returns only elements of type User stored in persons keyspace.
@KeySpace supports SpEL expressions allowing dynamic keyspace configuration.

5.2.0.M3

2.3.1. Custom KeySpace Annotation

You can compose your own KeySpace annotations for a more domain-centric usage by annotating one of the attributes with @AliasFor.

The composed annotation must inherit @Persistent.

The following example shows a custom @KeySpace annotation:

@KeySpace
@Persistent
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
static @interface CacheCentricAnnotation {

  @AliasFor(annotation = KeySpace.class, attribute = "value")
  String cacheRegion() default "";
}

@CacheCentricAnnotation(cacheRegion = "customers")
class Customer {
  //...
}

2.4. Querying

Running queries is managed by a QueryEngine. As mentioned earlier, you can instruct the KeyValueAdapter to use an implementation-specific QueryEngine that allows access to native functionality. When used without further customization, queries can be run by using SpELQueryEngine.

For performance reasons, we highly recommend to have at least Spring Framework 4.1.2 or better to make use of compiled SpEL Expressions. (“SpEL” is short for “Spring Expression Language”.) You can use the -Dspring.expression.compiler.mode=IMMEDIATE switch to enable it.

The following example shows a query that uses the SpEL:

KeyValueQuery<String> query = new KeyValueQuery<String>("lastname == 'targaryen'");
List<Person> targaryens = template.find(query, Person.class);
You must have getters and setters present to query properties when you use SpEL.

2.5. Sorting

Depending on the store implementation provided by the adapter, entities might already be stored in some sorted way but do not necessarily have to be.Again, the underlying QueryEngine is capable of performing sort operations. When used without further customization, sorting is done by using a SpelPropertyComparator extracted from the Sort clause.The following example shows a query with a Sort clause:

KeyValueQuery<String> query = new KeyValueQuery<String>("lastname == 'baratheon'");
query.setSort(Sort.by(DESC, "age"));
List<Person> targaryens = template.find(query, Person.class);
Please note that you need to have getters and setters present to sort using SpEL.

2.6. Map Repositories

Map repositories reside on top of the KeyValueTemplate. Using the default SpelQueryCreator allows deriving query and sort expressions from the given method name, as the following example shows:

@Configuration
@EnableMapRepositories
class KeyValueConfig {

}

interface PersonRepository implements CrudRepository<Person, String> {
    List<Person> findByLastname(String lastname);
}

Appendix

Unresolved directive in index.adoc - include::https://raw.githubusercontent.com/spring-projects/spring-data-commons/master/src/main/asciidoc/repository-namespace-reference.adoc[leveloffset=+1] Unresolved directive in index.adoc - include::https://raw.githubusercontent.com/spring-projects/spring-data-commons/master/src/main/asciidoc/repository-populator-namespace-reference.adoc[leveloffset=+1] Unresolved directive in index.adoc - include::https://raw.githubusercontent.com/spring-projects/spring-data-commons/master/src/main/asciidoc/repository-query-keywords-reference.adoc[leveloffset=+1]