diff --git a/spring-data-couchbase-2/.classpath b/spring-data-couchbase-2/.classpath
index f4c34adf1f..679a31b6da 100644
--- a/spring-data-couchbase-2/.classpath
+++ b/spring-data-couchbase-2/.classpath
@@ -4,7 +4,7 @@
-
+
diff --git a/spring-data-couchbase-2/src/main/java/org/baeldung/spring/data/couchbase/MyCouchbaseConfig.java b/spring-data-couchbase-2/src/main/java/org/baeldung/spring/data/couchbase/MyCouchbaseConfig.java
index 5c0b1a93ca..8e6b971dbf 100644
--- a/spring-data-couchbase-2/src/main/java/org/baeldung/spring/data/couchbase/MyCouchbaseConfig.java
+++ b/spring-data-couchbase-2/src/main/java/org/baeldung/spring/data/couchbase/MyCouchbaseConfig.java
@@ -3,9 +3,13 @@ package org.baeldung.spring.data.couchbase;
import java.util.Arrays;
import java.util.List;
+import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration;
+import org.springframework.data.couchbase.core.mapping.event.ValidatingCouchbaseEventListener;
+import org.springframework.data.couchbase.core.query.Consistency;
import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories;
+import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
@Configuration
@EnableCouchbaseRepositories(basePackages={"org.baeldung.spring.data.couchbase"})
@@ -29,4 +33,19 @@ public class MyCouchbaseConfig extends AbstractCouchbaseConfiguration {
protected String getBucketPassword() {
return BUCKET_PASSWORD;
}
+
+ @Override
+ protected Consistency getDefaultConsistency() {
+ return Consistency.READ_YOUR_OWN_WRITES;
+ }
+
+ @Bean
+ public LocalValidatorFactoryBean localValidatorFactoryBean() {
+ return new LocalValidatorFactoryBean();
+ }
+
+ @Bean
+ public ValidatingCouchbaseEventListener validatingCouchbaseEventListener() {
+ return new ValidatingCouchbaseEventListener(localValidatorFactoryBean());
+ }
}
diff --git a/spring-data-couchbase-2/src/main/java/org/baeldung/spring/data/couchbase/model/Student.java b/spring-data-couchbase-2/src/main/java/org/baeldung/spring/data/couchbase/model/Student.java
new file mode 100644
index 0000000000..9c266c2c62
--- /dev/null
+++ b/spring-data-couchbase-2/src/main/java/org/baeldung/spring/data/couchbase/model/Student.java
@@ -0,0 +1,113 @@
+package org.baeldung.spring.data.couchbase.model;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Past;
+import javax.validation.constraints.Pattern;
+import javax.validation.constraints.Size;
+
+import org.joda.time.DateTime;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.Version;
+import org.springframework.data.couchbase.core.mapping.Document;
+
+import com.couchbase.client.java.repository.annotation.Field;
+
+@Document
+public class Student {
+ private static final String NAME_REGEX = "^[a-zA-Z .'-]+$";
+
+ @Id
+ private String id;
+ @Field
+ @NotNull
+ @Size(min=1, max=20)
+ @Pattern(regexp=NAME_REGEX)
+ private String firstName;
+ @Field
+ @NotNull
+ @Size(min=1, max=20)
+ @Pattern(regexp=NAME_REGEX)
+ private String lastName;
+ @Field
+ @Past
+ private DateTime dateOfBirth;
+ @Field
+ @NotNull
+ private DateTime created;
+ @Field
+ private DateTime updated;
+ @Version
+ private long version;
+
+ public Student() {}
+
+ public Student(String id, String firstName, String lastName, DateTime dateOfBirth) {
+ this.id = id;
+ this.firstName = firstName;
+ this.lastName = lastName;
+ this.dateOfBirth = dateOfBirth;
+ }
+
+ 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;
+ }
+ public DateTime getDateOfBirth() {
+ return dateOfBirth;
+ }
+ public void setDateOfBirth(DateTime dateOfBirth) {
+ this.dateOfBirth = dateOfBirth;
+ }
+ public DateTime getCreated() {
+ return created;
+ }
+ public void setCreated(DateTime created) {
+ this.created = created;
+ }
+ public DateTime getUpdated() {
+ return updated;
+ }
+ public void setUpdated(DateTime updated) {
+ this.updated = updated;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 1;
+ if(id != null) {
+ hash = hash * 31 + id.hashCode();
+ }
+ if(firstName != null) {
+ hash = hash * 31 + firstName.hashCode();
+ }
+ if(lastName != null) {
+ hash = hash * 31 + lastName.hashCode();
+ }
+ if(dateOfBirth != null) {
+ hash = hash * 31 + dateOfBirth.hashCode();
+ }
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if((obj == null) || (obj.getClass() != this.getClass())) return false;
+ if(obj == this) return true;
+ Student other = (Student) obj;
+ return this.hashCode() == other.hashCode();
+ }
+}
\ No newline at end of file
diff --git a/spring-data-couchbase-2/src/main/java/org/baeldung/spring/data/couchbase/repos/CustomStudentRepository.java b/spring-data-couchbase-2/src/main/java/org/baeldung/spring/data/couchbase/repos/CustomStudentRepository.java
new file mode 100644
index 0000000000..9a5bf21492
--- /dev/null
+++ b/spring-data-couchbase-2/src/main/java/org/baeldung/spring/data/couchbase/repos/CustomStudentRepository.java
@@ -0,0 +1,9 @@
+package org.baeldung.spring.data.couchbase.repos;
+
+import java.util.List;
+
+import org.baeldung.spring.data.couchbase.model.Student;
+
+public interface CustomStudentRepository {
+ List findByFirstNameStartsWith(String s);
+}
diff --git a/spring-data-couchbase-2/src/main/java/org/baeldung/spring/data/couchbase/repos/CustomStudentRepositoryImpl.java b/spring-data-couchbase-2/src/main/java/org/baeldung/spring/data/couchbase/repos/CustomStudentRepositoryImpl.java
new file mode 100644
index 0000000000..66b672a4fd
--- /dev/null
+++ b/spring-data-couchbase-2/src/main/java/org/baeldung/spring/data/couchbase/repos/CustomStudentRepositoryImpl.java
@@ -0,0 +1,25 @@
+package org.baeldung.spring.data.couchbase.repos;
+
+import java.util.List;
+
+import org.baeldung.spring.data.couchbase.model.Student;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.couchbase.core.CouchbaseTemplate;
+
+import com.couchbase.client.java.view.Stale;
+import com.couchbase.client.java.view.ViewQuery;
+
+public class CustomStudentRepositoryImpl implements CustomStudentRepository {
+
+ private static final String DESIGN_DOC = "student";
+
+ @Autowired
+ private CouchbaseTemplate template;
+
+ public List findByFirstNameStartsWith(String s) {
+ return template.findByView(ViewQuery.from(DESIGN_DOC, "byFirstName")
+ .startKey(s)
+ .stale(Stale.FALSE),
+ Student.class);
+ }
+}
diff --git a/spring-data-couchbase-2/src/main/java/org/baeldung/spring/data/couchbase/repos/StudentRepository.java b/spring-data-couchbase-2/src/main/java/org/baeldung/spring/data/couchbase/repos/StudentRepository.java
new file mode 100644
index 0000000000..331ddc553d
--- /dev/null
+++ b/spring-data-couchbase-2/src/main/java/org/baeldung/spring/data/couchbase/repos/StudentRepository.java
@@ -0,0 +1,11 @@
+package org.baeldung.spring.data.couchbase.repos;
+
+import java.util.List;
+
+import org.baeldung.spring.data.couchbase.model.Student;
+import org.springframework.data.repository.CrudRepository;
+
+public interface StudentRepository extends CrudRepository, CustomStudentRepository {
+ List findByFirstName(String firstName);
+ List findByLastName(String lastName);
+}
diff --git a/spring-data-couchbase-2/src/main/java/org/baeldung/spring/data/couchbase/service/StudentRepositoryService.java b/spring-data-couchbase-2/src/main/java/org/baeldung/spring/data/couchbase/service/StudentRepositoryService.java
new file mode 100644
index 0000000000..58304afc1c
--- /dev/null
+++ b/spring-data-couchbase-2/src/main/java/org/baeldung/spring/data/couchbase/service/StudentRepositoryService.java
@@ -0,0 +1,58 @@
+package org.baeldung.spring.data.couchbase.service;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.baeldung.spring.data.couchbase.model.Student;
+import org.baeldung.spring.data.couchbase.repos.StudentRepository;
+import org.joda.time.DateTime;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Service;
+
+@Service
+@Qualifier("StudentRepositoryService")
+public class StudentRepositoryService implements StudentService {
+
+ private StudentRepository repo;
+ @Autowired
+ public void setStudentRepository(StudentRepository repo) {
+ this.repo = repo;
+ }
+
+ public Student findOne(String id) {
+ return repo.findOne(id);
+ }
+
+ public List findAll() {
+ List people = new ArrayList();
+ Iterator it = repo.findAll().iterator();
+ while(it.hasNext()) {
+ people.add(it.next());
+ }
+ return people;
+ }
+
+ public List findByFirstName(String firstName) {
+ return repo.findByFirstName(firstName);
+ }
+
+ public List findByLastName(String lastName) {
+ return repo.findByLastName(lastName);
+ }
+
+ public void create(Student student) {
+ student.setCreated(DateTime.now());
+ repo.save(student);
+ }
+
+ public void update(Student student) {
+ student.setUpdated(DateTime.now());
+ repo.save(student);
+ }
+
+ public void delete(Student student) {
+ repo.delete(student);
+ }
+}
diff --git a/spring-data-couchbase-2/src/main/java/org/baeldung/spring/data/couchbase/service/StudentService.java b/spring-data-couchbase-2/src/main/java/org/baeldung/spring/data/couchbase/service/StudentService.java
new file mode 100644
index 0000000000..f483ef0fb6
--- /dev/null
+++ b/spring-data-couchbase-2/src/main/java/org/baeldung/spring/data/couchbase/service/StudentService.java
@@ -0,0 +1,22 @@
+package org.baeldung.spring.data.couchbase.service;
+
+import java.util.List;
+
+import org.baeldung.spring.data.couchbase.model.Student;
+
+public interface StudentService {
+
+ Student findOne(String id);
+
+ List findAll();
+
+ List findByFirstName(String firstName);
+
+ List findByLastName(String lastName);
+
+ void create(Student student);
+
+ void update(Student student);
+
+ void delete(Student student);
+}
diff --git a/spring-data-couchbase-2/src/main/java/org/baeldung/spring/data/couchbase/service/StudentTemplateService.java b/spring-data-couchbase-2/src/main/java/org/baeldung/spring/data/couchbase/service/StudentTemplateService.java
new file mode 100644
index 0000000000..c3808e0015
--- /dev/null
+++ b/spring-data-couchbase-2/src/main/java/org/baeldung/spring/data/couchbase/service/StudentTemplateService.java
@@ -0,0 +1,55 @@
+package org.baeldung.spring.data.couchbase.service;
+
+import java.util.List;
+
+import org.baeldung.spring.data.couchbase.model.Student;
+import org.joda.time.DateTime;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.data.couchbase.core.CouchbaseTemplate;
+import org.springframework.stereotype.Service;
+
+import com.couchbase.client.java.view.ViewQuery;
+
+@Service
+@Qualifier("StudentTemplateService")
+public class StudentTemplateService implements StudentService {
+
+ private static final String DESIGN_DOC = "student";
+
+ private CouchbaseTemplate template;
+ @Autowired
+ public void setCouchbaseTemplate(CouchbaseTemplate template) {
+ this.template = template;
+ }
+
+ public Student findOne(String id) {
+ return template.findById(id, Student.class);
+ }
+
+ public List findAll() {
+ return template.findByView(ViewQuery.from(DESIGN_DOC, "all"), Student.class);
+ }
+
+ public List findByFirstName(String firstName) {
+ return template.findByView(ViewQuery.from(DESIGN_DOC, "byFirstName"), Student.class);
+ }
+
+ public List findByLastName(String lastName) {
+ return template.findByView(ViewQuery.from(DESIGN_DOC, "byLastName"), Student.class);
+ }
+
+ public void create(Student student) {
+ student.setCreated(DateTime.now());
+ template.insert(student);
+ }
+
+ public void update(Student student) {
+ student.setUpdated(DateTime.now());
+ template.update(student);
+ }
+
+ public void delete(Student student) {
+ template.remove(student);
+ }
+}
diff --git a/spring-data-couchbase-2/src/test/java/org/baeldung/spring/data/couchbase/TestCouchbaseConfig.java b/spring-data-couchbase-2/src/test/java/org/baeldung/spring/data/couchbase/TestCouchbaseConfig.java
index 4edfb165bf..c298ef0a61 100644
--- a/spring-data-couchbase-2/src/test/java/org/baeldung/spring/data/couchbase/TestCouchbaseConfig.java
+++ b/spring-data-couchbase-2/src/test/java/org/baeldung/spring/data/couchbase/TestCouchbaseConfig.java
@@ -1,6 +1,7 @@
package org.baeldung.spring.data.couchbase;
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
+import org.springframework.data.couchbase.core.query.Consistency;
public class TestCouchbaseConfig extends MyCouchbaseConfig {
@@ -8,4 +9,9 @@ public class TestCouchbaseConfig extends MyCouchbaseConfig {
public String typeKey() {
return MappingCouchbaseConverter.TYPEKEY_SYNCGATEWAY_COMPATIBLE;
}
+
+ @Override
+ public Consistency getDefaultConsistency() {
+ return Consistency.READ_YOUR_OWN_WRITES;
+ }
}
diff --git a/spring-data-couchbase-2/src/test/java/org/baeldung/spring/data/couchbase/service/StudentRepositoryServiceTest.java b/spring-data-couchbase-2/src/test/java/org/baeldung/spring/data/couchbase/service/StudentRepositoryServiceTest.java
new file mode 100644
index 0000000000..040453fd73
--- /dev/null
+++ b/spring-data-couchbase-2/src/test/java/org/baeldung/spring/data/couchbase/service/StudentRepositoryServiceTest.java
@@ -0,0 +1,13 @@
+package org.baeldung.spring.data.couchbase.service;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+
+public class StudentRepositoryServiceTest extends StudentServiceTest {
+
+ @Autowired
+ @Qualifier("StudentRepositoryService")
+ public void setStudentService(StudentService service) {
+ this.studentService = service;
+ }
+}
diff --git a/spring-data-couchbase-2/src/test/java/org/baeldung/spring/data/couchbase/service/StudentServiceTest.java b/spring-data-couchbase-2/src/test/java/org/baeldung/spring/data/couchbase/service/StudentServiceTest.java
new file mode 100644
index 0000000000..79948cbedf
--- /dev/null
+++ b/spring-data-couchbase-2/src/test/java/org/baeldung/spring/data/couchbase/service/StudentServiceTest.java
@@ -0,0 +1,166 @@
+package org.baeldung.spring.data.couchbase.service;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import javax.validation.ConstraintViolationException;
+
+import org.baeldung.spring.data.couchbase.IntegrationTest;
+import org.baeldung.spring.data.couchbase.MyCouchbaseConfig;
+import org.baeldung.spring.data.couchbase.model.Student;
+import org.joda.time.DateTime;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.couchbase.client.java.Bucket;
+import com.couchbase.client.java.Cluster;
+import com.couchbase.client.java.CouchbaseCluster;
+import com.couchbase.client.java.document.JsonDocument;
+import com.couchbase.client.java.document.json.JsonObject;
+
+public abstract class StudentServiceTest extends IntegrationTest {
+
+ static final String typeField = "_class";
+ static final String joe = "Joe";
+ static final String college = "College";
+ static final String joeCollegeId = "student:" + joe + ":" + college;
+ static final DateTime joeCollegeDob = DateTime.now().minusYears(21);
+ static final Student joeCollege = new Student(joeCollegeId, joe, college, joeCollegeDob);
+ static final JsonObject jsonJoeCollege = JsonObject.empty()
+ .put(typeField, Student.class.getName())
+ .put("firstName", joe)
+ .put("lastName", college)
+ .put("created", DateTime.now().getMillis())
+ .put("version", 1);
+
+ static final String judy = "Judy";
+ static final String jetson = "Jetson";
+ static final String judyJetsonId = "student:" + judy + ":" + jetson;
+ static final DateTime judyJetsonDob = DateTime.now().minusYears(19).minusMonths(5).minusDays(3);
+ static final Student judyJetson = new Student(judyJetsonId, judy, jetson, judyJetsonDob);
+ static final JsonObject jsonJudyJetson = JsonObject.empty()
+ .put(typeField, Student.class.getName())
+ .put("firstName", judy)
+ .put("lastName", jetson)
+ .put("created", DateTime.now().getMillis())
+ .put("version", 1);
+
+ StudentService studentService;
+
+ @BeforeClass
+ public static void setupBeforeClass() {
+ Cluster cluster = CouchbaseCluster.create(MyCouchbaseConfig.NODE_LIST);
+ Bucket bucket = cluster.openBucket(MyCouchbaseConfig.BUCKET_NAME, MyCouchbaseConfig.BUCKET_PASSWORD);
+ bucket.upsert(JsonDocument.create(joeCollegeId, jsonJoeCollege));
+ bucket.upsert(JsonDocument.create(judyJetsonId, jsonJudyJetson));
+ bucket.close();
+ cluster.disconnect();
+ }
+
+ @Test
+ public void whenCreatingStudent_thenDocumentIsPersisted() {
+ String firstName = "Eric";
+ String lastName = "Stratton";
+ DateTime dateOfBirth = DateTime.now().minusYears(25);
+ String id = "student:" + firstName + ":" + lastName;
+ Student expectedStudent = new Student(id, firstName, lastName, dateOfBirth);
+ studentService.create(expectedStudent);
+ Student actualStudent = studentService.findOne(id);
+ assertNotNull(actualStudent.getCreated());
+ assertNotNull(actualStudent);
+ assertEquals(expectedStudent.getId(), actualStudent.getId());
+ }
+
+ @Test(expected=ConstraintViolationException.class)
+ public void whenCreatingStudentWithInvalidFirstName_thenConstraintViolationException() {
+ String firstName = "Er+ic";
+ String lastName = "Stratton";
+ DateTime dateOfBirth = DateTime.now().minusYears(25);
+ String id = "student:" + firstName + ":" + lastName;
+ Student student = new Student(id, firstName, lastName, dateOfBirth);
+ studentService.create(student);
+ }
+
+ @Test(expected=ConstraintViolationException.class)
+ public void whenCreatingStudentWithFutureDob_thenConstraintViolationException() {
+ String firstName = "Jane";
+ String lastName = "Doe";
+ DateTime dateOfBirth = DateTime.now().plusDays(1);
+ String id = "student:" + firstName + ":" + lastName;
+ Student student = new Student(id, firstName, lastName, dateOfBirth);
+ studentService.create(student);
+ }
+
+ @Test
+ public void whenFindingStudentByJohnSmithId_thenReturnsJohnSmith() {
+ Student actualStudent = studentService.findOne(joeCollegeId);
+ assertNotNull(actualStudent);
+ assertNotNull(actualStudent.getCreated());
+ assertEquals(joeCollegeId, actualStudent.getId());
+ }
+
+ @Test
+ public void whenFindingAllStudents_thenReturnsTwoOrMoreStudentsIncludingJoeCollegeAndJudyJetson() {
+ List resultList = studentService.findAll();
+ assertNotNull(resultList);
+ assertFalse(resultList.isEmpty());
+ assertTrue(resultContains(resultList, joeCollege));
+ assertTrue(resultContains(resultList, judyJetson));
+ assertTrue(resultList.size() >= 2);
+ }
+
+ @Test
+ public void whenFindingByFirstNameJohn_thenReturnsOnlyStudentsNamedJohn() {
+ String expectedFirstName = joe;
+ List resultList = studentService.findByFirstName(expectedFirstName);
+ assertNotNull(resultList);
+ assertFalse(resultList.isEmpty());
+ assertTrue(allResultsContainExpectedFirstName(resultList, expectedFirstName));
+ }
+
+ @Test
+ public void whenFindingByLastNameSmith_thenReturnsOnlyStudentsNamedSmith() {
+ String expectedLastName = college;
+ List resultList = studentService.findByLastName(expectedLastName);
+ assertNotNull(resultList);
+ assertFalse(resultList.isEmpty());
+ assertTrue(allResultsContainExpectedLastName(resultList, expectedLastName));
+ }
+
+ private boolean resultContains(List resultList, Student student) {
+ boolean found = false;
+ for(Student p : resultList) {
+ if(p.getId().equals(student.getId())) {
+ found = true;
+ break;
+ }
+ }
+ return found;
+ }
+
+ private boolean allResultsContainExpectedFirstName(List resultList, String firstName) {
+ boolean found = false;
+ for(Student p : resultList) {
+ if(p.getFirstName().equals(firstName)) {
+ found = true;
+ break;
+ }
+ }
+ return found;
+ }
+
+ private boolean allResultsContainExpectedLastName(List resultList, String lastName) {
+ boolean found = false;
+ for(Student p : resultList) {
+ if(p.getLastName().equals(lastName)) {
+ found = true;
+ break;
+ }
+ }
+ return found;
+ }
+}
diff --git a/spring-data-couchbase-2/src/test/java/org/baeldung/spring/data/couchbase/service/StudentTemplateServiceTest.java b/spring-data-couchbase-2/src/test/java/org/baeldung/spring/data/couchbase/service/StudentTemplateServiceTest.java
new file mode 100644
index 0000000000..dd5be8e059
--- /dev/null
+++ b/spring-data-couchbase-2/src/test/java/org/baeldung/spring/data/couchbase/service/StudentTemplateServiceTest.java
@@ -0,0 +1,13 @@
+package org.baeldung.spring.data.couchbase.service;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+
+public class StudentTemplateServiceTest extends StudentServiceTest {
+
+ @Autowired
+ @Qualifier("StudentTemplateService")
+ public void setStudentService(StudentService service) {
+ this.studentService = service;
+ }
+}