diff --git a/pom.xml b/pom.xml
index 3ba6b0c8dfb1..ea1f5e1f94d0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -152,6 +152,7 @@
spring-data-couchbase-2
persistence-modules/spring-data-dynamodb
spring-data-elasticsearch
+ spring-data-keyvalue
spring-data-mongodb
persistence-modules/spring-data-neo4j
persistence-modules/spring-data-redis
diff --git a/spring-data-keyvalue/pom.xml b/spring-data-keyvalue/pom.xml
new file mode 100644
index 000000000000..e90f18948f51
--- /dev/null
+++ b/spring-data-keyvalue/pom.xml
@@ -0,0 +1,37 @@
+
+ 4.0.0
+ com.baeldung
+ spring-data-keyvalue
+ 1.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.0.1.RELEASE
+
+
+
+
+ 2.0.3.RELEASE
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.springframework.data
+ spring-data-keyvalue
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+
+
+
+
\ No newline at end of file
diff --git a/spring-data-keyvalue/src/main/java/com/baeldung/spring/data/keyvalue/Configurations.java b/spring-data-keyvalue/src/main/java/com/baeldung/spring/data/keyvalue/Configurations.java
new file mode 100644
index 000000000000..c762c00333b8
--- /dev/null
+++ b/spring-data-keyvalue/src/main/java/com/baeldung/spring/data/keyvalue/Configurations.java
@@ -0,0 +1,28 @@
+package com.baeldung.spring.data.keyvalue;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.keyvalue.core.KeyValueAdapter;
+import org.springframework.data.keyvalue.core.KeyValueOperations;
+import org.springframework.data.keyvalue.core.KeyValueTemplate;
+import org.springframework.data.map.MapKeyValueAdapter;
+
+@Configuration
+public class Configurations {
+
+ //To be used only if @EnableMapRepositories is not used.
+ //Else @EnableMapRepositories gives us a template as well.
+ @Bean("keyValueTemplate")
+ public KeyValueOperations keyValueTemplate() {
+ return new KeyValueTemplate(keyValueAdapter());
+
+ }
+
+ @Bean
+ public KeyValueAdapter keyValueAdapter() {
+ return new MapKeyValueAdapter(ConcurrentHashMap.class);
+ }
+
+}
diff --git a/spring-data-keyvalue/src/main/java/com/baeldung/spring/data/keyvalue/SpringDataKeyValueApplication.java b/spring-data-keyvalue/src/main/java/com/baeldung/spring/data/keyvalue/SpringDataKeyValueApplication.java
new file mode 100644
index 000000000000..6a5b5264df31
--- /dev/null
+++ b/spring-data-keyvalue/src/main/java/com/baeldung/spring/data/keyvalue/SpringDataKeyValueApplication.java
@@ -0,0 +1,15 @@
+package com.baeldung.spring.data.keyvalue;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.data.map.repository.config.EnableMapRepositories;
+
+@SpringBootApplication
+@EnableMapRepositories
+public class SpringDataKeyValueApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SpringDataKeyValueApplication.class, args);
+ }
+
+}
diff --git a/spring-data-keyvalue/src/main/java/com/baeldung/spring/data/keyvalue/repositories/EmployeeRepository.java b/spring-data-keyvalue/src/main/java/com/baeldung/spring/data/keyvalue/repositories/EmployeeRepository.java
new file mode 100644
index 000000000000..5b7545d5b641
--- /dev/null
+++ b/spring-data-keyvalue/src/main/java/com/baeldung/spring/data/keyvalue/repositories/EmployeeRepository.java
@@ -0,0 +1,10 @@
+package com.baeldung.spring.data.keyvalue.repositories;
+
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
+
+import com.baeldung.spring.data.keyvalue.vo.Employee;
+
+@Repository("employeeRepository")
+public interface EmployeeRepository extends CrudRepository {
+}
\ No newline at end of file
diff --git a/spring-data-keyvalue/src/main/java/com/baeldung/spring/data/keyvalue/services/EmployeeService.java b/spring-data-keyvalue/src/main/java/com/baeldung/spring/data/keyvalue/services/EmployeeService.java
new file mode 100644
index 000000000000..dd89609be7bd
--- /dev/null
+++ b/spring-data-keyvalue/src/main/java/com/baeldung/spring/data/keyvalue/services/EmployeeService.java
@@ -0,0 +1,19 @@
+package com.baeldung.spring.data.keyvalue.services;
+
+import com.baeldung.spring.data.keyvalue.vo.Employee;
+
+public interface EmployeeService {
+
+ void save(Employee employee);
+
+ Employee get(Integer id);
+
+ Iterable fetchAll();
+
+ void update(Employee employee);
+
+ void delete(Integer id);
+
+ Iterable getSortedListOfEmployeesBySalary();
+
+}
diff --git a/spring-data-keyvalue/src/main/java/com/baeldung/spring/data/keyvalue/services/impl/EmployeeServicesWithKeyValueTemplate.java b/spring-data-keyvalue/src/main/java/com/baeldung/spring/data/keyvalue/services/impl/EmployeeServicesWithKeyValueTemplate.java
new file mode 100644
index 000000000000..26f1756add59
--- /dev/null
+++ b/spring-data-keyvalue/src/main/java/com/baeldung/spring/data/keyvalue/services/impl/EmployeeServicesWithKeyValueTemplate.java
@@ -0,0 +1,57 @@
+package com.baeldung.spring.data.keyvalue.services.impl;
+
+import java.util.Optional;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.DependsOn;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.keyvalue.core.KeyValueTemplate;
+import org.springframework.data.keyvalue.core.query.KeyValueQuery;
+import org.springframework.stereotype.Service;
+
+import com.baeldung.spring.data.keyvalue.services.EmployeeService;
+import com.baeldung.spring.data.keyvalue.vo.Employee;
+
+@Service("employeeServicesWithKeyValueTemplate")
+@DependsOn("keyValueTemplate")
+public class EmployeeServicesWithKeyValueTemplate implements EmployeeService {
+
+ @Autowired
+ @Qualifier("keyValueTemplate")
+ KeyValueTemplate keyValueTemplate;
+
+ @Override
+ public void save(Employee employee) {
+ keyValueTemplate.insert(employee);
+ }
+
+ @Override
+ public Employee get(Integer id) {
+ Optional employee = keyValueTemplate.findById(id, Employee.class);
+ return employee.isPresent() ? employee.get() : null;
+ }
+
+ @Override
+ public Iterable fetchAll() {
+ return keyValueTemplate.findAll(Employee.class);
+ }
+
+ @Override
+ public void update(Employee employee) {
+ keyValueTemplate.update(employee);
+ }
+
+ @Override
+ public void delete(Integer id) {
+ keyValueTemplate.delete(id, Employee.class);
+ }
+
+ @Override
+ public Iterable getSortedListOfEmployeesBySalary() {
+ KeyValueQuery query = new KeyValueQuery();
+ query.setSort(new Sort(Sort.Direction.DESC, "salary"));
+ return keyValueTemplate.find(query, Employee.class);
+ }
+
+}
diff --git a/spring-data-keyvalue/src/main/java/com/baeldung/spring/data/keyvalue/services/impl/EmployeeServicesWithRepository.java b/spring-data-keyvalue/src/main/java/com/baeldung/spring/data/keyvalue/services/impl/EmployeeServicesWithRepository.java
new file mode 100644
index 000000000000..73f3493a6b49
--- /dev/null
+++ b/spring-data-keyvalue/src/main/java/com/baeldung/spring/data/keyvalue/services/impl/EmployeeServicesWithRepository.java
@@ -0,0 +1,48 @@
+package com.baeldung.spring.data.keyvalue.services.impl;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.baeldung.spring.data.keyvalue.repositories.EmployeeRepository;
+import com.baeldung.spring.data.keyvalue.services.EmployeeService;
+import com.baeldung.spring.data.keyvalue.vo.Employee;
+
+@Service("employeeServicesWithRepository")
+public class EmployeeServicesWithRepository implements EmployeeService {
+
+ @Autowired
+ EmployeeRepository employeeRepository;
+
+
+ @Override
+ public void save(Employee employee) {
+ employeeRepository.save(employee);
+ }
+
+ @Override
+ public Iterable fetchAll() {
+ return employeeRepository.findAll();
+
+ }
+
+ @Override
+ public Employee get(Integer id) {
+ return employeeRepository.findById(id).get();
+ }
+
+ @Override
+ public void update(Employee employee) {
+ employeeRepository.save(employee);
+
+ }
+
+ @Override
+ public void delete(Integer id) {
+ employeeRepository.deleteById(id);
+ }
+
+ public Iterable getSortedListOfEmployeesBySalary() {
+ throw new RuntimeException("Method not supported by CRUDRepository");
+ }
+
+}
diff --git a/spring-data-keyvalue/src/main/java/com/baeldung/spring/data/keyvalue/vo/Employee.java b/spring-data-keyvalue/src/main/java/com/baeldung/spring/data/keyvalue/vo/Employee.java
new file mode 100644
index 000000000000..208e6e173580
--- /dev/null
+++ b/spring-data-keyvalue/src/main/java/com/baeldung/spring/data/keyvalue/vo/Employee.java
@@ -0,0 +1,68 @@
+package com.baeldung.spring.data.keyvalue.vo;
+
+import java.io.Serializable;
+
+import org.springframework.data.annotation.Id;
+import org.springframework.data.keyvalue.annotation.KeySpace;
+
+@KeySpace("employees")
+public class Employee implements Serializable {
+
+ @Id
+ private Integer id;
+
+ private String name;
+
+ private String department;
+
+ private String salary;
+
+ public Employee(Integer id, String name, String department, String salary) {
+ this.id = id;
+ this.name = name;
+ this.department = department;
+ this.salary = salary;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDepartment() {
+ return department;
+ }
+
+ public void setDepartment(String department) {
+ this.department = department;
+ }
+
+ public String getSalary() {
+ return salary;
+ }
+
+ public void setSalary(String salary) {
+ this.salary = salary;
+ }
+
+ @Override
+ public String toString() {
+ return "Employee{" +
+ "id=" + id +
+ ", name='" + name + '\'' +
+ ", department='" + department + '\'' +
+ ", salary='" + salary + '\'' +
+ '}';
+ }
+}
diff --git a/spring-data-keyvalue/src/test/java/com/baeldung/spring/data/keyvalue/services/test/EmployeeServicesWithKeyValueTemplateTest.java b/spring-data-keyvalue/src/test/java/com/baeldung/spring/data/keyvalue/services/test/EmployeeServicesWithKeyValueTemplateTest.java
new file mode 100644
index 000000000000..78e985fe4b6e
--- /dev/null
+++ b/spring-data-keyvalue/src/test/java/com/baeldung/spring/data/keyvalue/services/test/EmployeeServicesWithKeyValueTemplateTest.java
@@ -0,0 +1,88 @@
+package com.baeldung.spring.data.keyvalue.services.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.List;
+
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.keyvalue.core.KeyValueTemplate;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import com.baeldung.spring.data.keyvalue.SpringDataKeyValueApplication;
+import com.baeldung.spring.data.keyvalue.services.EmployeeService;
+import com.baeldung.spring.data.keyvalue.vo.Employee;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringBootTest(classes = SpringDataKeyValueApplication.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class EmployeeServicesWithKeyValueTemplateTest {
+
+ @Autowired
+ @Qualifier("employeeServicesWithKeyValueTemplate")
+ EmployeeService employeeService;
+
+ @Autowired
+ @Qualifier("keyValueTemplate")
+ KeyValueTemplate keyValueTemplate;
+
+ static Employee employee1;
+
+ static Employee employee2;
+
+ @BeforeClass
+ public static void setUp() {
+ employee1 = new Employee(1, "Karan", "IT", "5000");
+ employee2 = new Employee(2, "Jack", "HR", "2000");
+ }
+
+ @Test
+ public void test1_whenEmployeeSaved_thenEmployeeIsAddedToMap() {
+ employeeService.save(employee1);
+ assertEquals(keyValueTemplate.findById(1, Employee.class).get(), employee1);
+ }
+
+ @Test
+ public void test2_whenEmployeeGet_thenEmployeeIsReturnedFromMap() {
+ Employee employeeFetched = employeeService.get(1);
+ assertEquals(employeeFetched, employee1);
+ }
+
+ @Test
+ public void test3_whenEmployeesFetched_thenEmployeesAreReturnedFromMap() {
+ List employees = (List)employeeService.fetchAll();
+ assertEquals(employees.size(), 1);
+ assertEquals(employees.get(0), employee1);
+ }
+
+ @Test
+ public void test4_whenEmployeeUpdated_thenEmployeeIsUpdatedToMap() {
+ employee1.setName("Pawan");
+ employeeService.update(employee1);
+ assertEquals(keyValueTemplate.findById(1, Employee.class).get().getName(),"Pawan");
+ }
+
+ @Test
+ public void test5_whenSortedEmployeesFetched_thenEmployeesAreReturnedFromMapInOrder() {
+ employeeService.save(employee2);
+ List employees = (List)employeeService.getSortedListOfEmployeesBySalary();
+ assertEquals(employees.size(), 2);
+ assertEquals(employees.get(0), employee1);
+ assertEquals(employees.get(1), employee2);
+ }
+
+ @Test
+ public void test6_whenEmployeeDeleted_thenEmployeeIsRemovedMap() {
+ employeeService.delete(1);
+ assertEquals(keyValueTemplate.findById(1, Employee.class).isPresent(), false);
+ }
+
+
+
+}
diff --git a/spring-data-keyvalue/src/test/java/com/baeldung/spring/data/keyvalue/services/test/EmployeeServicesWithRepositoryTest.java b/spring-data-keyvalue/src/test/java/com/baeldung/spring/data/keyvalue/services/test/EmployeeServicesWithRepositoryTest.java
new file mode 100644
index 000000000000..5b3272889352
--- /dev/null
+++ b/spring-data-keyvalue/src/test/java/com/baeldung/spring/data/keyvalue/services/test/EmployeeServicesWithRepositoryTest.java
@@ -0,0 +1,73 @@
+package com.baeldung.spring.data.keyvalue.services.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.List;
+
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import com.baeldung.spring.data.keyvalue.SpringDataKeyValueApplication;
+import com.baeldung.spring.data.keyvalue.repositories.EmployeeRepository;
+import com.baeldung.spring.data.keyvalue.services.EmployeeService;
+import com.baeldung.spring.data.keyvalue.vo.Employee;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringBootTest(classes = SpringDataKeyValueApplication.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class EmployeeServicesWithRepositoryTest {
+
+ @Autowired
+ @Qualifier("employeeServicesWithRepository")
+ EmployeeService employeeService;
+
+ @Autowired
+ EmployeeRepository employeeRepository;
+
+ static Employee employee1;
+
+ @BeforeClass
+ public static void setUp() {
+ employee1 = new Employee(1, "Karan", "IT", "5000");
+ }
+
+ @Test
+ public void test1_whenEmployeeSaved_thenEmployeeIsAddedToMap() {
+ employeeService.save(employee1);
+ assertEquals(employeeRepository.findById(1).get(), employee1);
+ }
+
+ @Test
+ public void test2_whenEmployeeGet_thenEmployeeIsReturnedFromMap() {
+ Employee employeeFetched = employeeService.get(1);
+ assertEquals(employeeFetched, employee1);
+ }
+
+ @Test
+ public void test3_whenEmployeesFetched_thenEmployeesAreReturnedFromMap() {
+ List employees = (List)employeeService.fetchAll();
+ assertEquals(employees.size(), 1);
+ assertEquals(employees.get(0), employee1);
+ }
+
+ @Test
+ public void test4_whenEmployeeUpdated_thenEmployeeIsUpdatedToMap() {
+ employee1.setName("Pawan");
+ employeeService.update(employee1);
+ assertEquals(employeeRepository.findById(1).get().getName(),"Pawan");
+ }
+
+ @Test
+ public void test5_whenEmployeeDeleted_thenEmployeeIsRemovedMap() {
+ employeeService.delete(1);
+ assertEquals(employeeRepository.findById(1).isPresent(), false);
+ }
+
+}