Merge pull request #405 from KevinGilmore/master

2nd Spring Data Couchbase Article
This commit is contained in:
Eugen 2016-05-28 21:16:59 +01:00
commit abb5490682
13 changed files with 511 additions and 1 deletions

View File

@ -4,7 +4,7 @@
<classpathentry kind="src" path="src/main/java"/>
<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
<classpathentry kind="src" path="src/test/resources"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@ -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());
}
}

View File

@ -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();
}
}

View File

@ -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<Student> findByFirstNameStartsWith(String s);
}

View File

@ -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<Student> findByFirstNameStartsWith(String s) {
return template.findByView(ViewQuery.from(DESIGN_DOC, "byFirstName")
.startKey(s)
.stale(Stale.FALSE),
Student.class);
}
}

View File

@ -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<Student, String>, CustomStudentRepository {
List<Student> findByFirstName(String firstName);
List<Student> findByLastName(String lastName);
}

View File

@ -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<Student> findAll() {
List<Student> people = new ArrayList<Student>();
Iterator<Student> it = repo.findAll().iterator();
while(it.hasNext()) {
people.add(it.next());
}
return people;
}
public List<Student> findByFirstName(String firstName) {
return repo.findByFirstName(firstName);
}
public List<Student> 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);
}
}

View File

@ -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<Student> findAll();
List<Student> findByFirstName(String firstName);
List<Student> findByLastName(String lastName);
void create(Student student);
void update(Student student);
void delete(Student student);
}

View File

@ -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<Student> findAll() {
return template.findByView(ViewQuery.from(DESIGN_DOC, "all"), Student.class);
}
public List<Student> findByFirstName(String firstName) {
return template.findByView(ViewQuery.from(DESIGN_DOC, "byFirstName"), Student.class);
}
public List<Student> 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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<Student> 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<Student> resultList = studentService.findByFirstName(expectedFirstName);
assertNotNull(resultList);
assertFalse(resultList.isEmpty());
assertTrue(allResultsContainExpectedFirstName(resultList, expectedFirstName));
}
@Test
public void whenFindingByLastNameSmith_thenReturnsOnlyStudentsNamedSmith() {
String expectedLastName = college;
List<Student> resultList = studentService.findByLastName(expectedLastName);
assertNotNull(resultList);
assertFalse(resultList.isEmpty());
assertTrue(allResultsContainExpectedLastName(resultList, expectedLastName));
}
private boolean resultContains(List<Student> 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<Student> resultList, String firstName) {
boolean found = false;
for(Student p : resultList) {
if(p.getFirstName().equals(firstName)) {
found = true;
break;
}
}
return found;
}
private boolean allResultsContainExpectedLastName(List<Student> resultList, String lastName) {
boolean found = false;
for(Student p : resultList) {
if(p.getLastName().equals(lastName)) {
found = true;
break;
}
}
return found;
}
}

View File

@ -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;
}
}