diff --git a/pom.xml b/pom.xml
index 0d37407ad..151ad8c7f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,6 +21,7 @@
1.2.0
2.2.3
1.6.1.RELEASE
+ 1.3
@@ -87,6 +88,13 @@
${jackson}
+
+ org.hamcrest
+ hamcrest-all
+ ${hamcrest}
+ test
+
+
diff --git a/src/main/java/org/springframework/data/couchbase/core/view/View.java b/src/main/java/org/springframework/data/couchbase/core/view/View.java
new file mode 100644
index 000000000..8cd975d33
--- /dev/null
+++ b/src/main/java/org/springframework/data/couchbase/core/view/View.java
@@ -0,0 +1,35 @@
+package org.springframework.data.couchbase.core.view;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to support the use of Views with Couchbase.
+ */
+@Documented
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface View {
+
+ /**
+ * The name of the Design document to use.
+ *
+ * This field is mandatory.
+ *
+ * @return name of the Design document.
+ */
+ String design();
+
+ /**
+ * The name of the View to use.
+ *
+ * This field is mandatory.
+ *
+ * @return name of the View
+ */
+ String view();
+
+}
diff --git a/src/main/java/org/springframework/data/couchbase/repository/support/CouchbaseRepositoryFactory.java b/src/main/java/org/springframework/data/couchbase/repository/support/CouchbaseRepositoryFactory.java
index 6782ea504..3e736eba3 100644
--- a/src/main/java/org/springframework/data/couchbase/repository/support/CouchbaseRepositoryFactory.java
+++ b/src/main/java/org/springframework/data/couchbase/repository/support/CouchbaseRepositoryFactory.java
@@ -22,6 +22,7 @@
import org.springframework.data.couchbase.repository.query.CouchbaseEntityInformation;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.MappingException;
+import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.util.Assert;
@@ -63,16 +64,17 @@ public CouchbaseRepositoryFactory(final CouchbaseOperations couchbaseOperations)
* @param domainClass the class for the entity.
* @param the value type
* @param the id type.
+ *
* @return entity information for that domain class.
*/
@Override
public CouchbaseEntityInformation
- getEntityInformation(final Class domainClass) {
+ getEntityInformation(final Class domainClass) {
CouchbasePersistentEntity> entity = mappingContext.getPersistentEntity(domainClass);
if (entity == null) {
throw new MappingException(String.format("Could not lookup mapping metadata for domain class %s!",
- domainClass.getName()));
+ domainClass.getName()));
}
return new MappingCouchbaseEntityInformation((CouchbasePersistentEntity) entity);
}
@@ -81,23 +83,26 @@ public CouchbaseRepositoryFactory(final CouchbaseOperations couchbaseOperations)
* Returns a new Repository based on the metadata.
*
* @param metadata the repository metadata.
+ *
* @return a new created repository.
*/
@Override
protected Object getTargetRepository(final RepositoryMetadata metadata) {
- CouchbaseEntityInformation, Serializable> entityInformation =
- getEntityInformation(metadata.getDomainType());
- return new SimpleCouchbaseRepository(entityInformation, couchbaseOperations);
+ CouchbaseEntityInformation, Serializable> entityInformation = getEntityInformation(metadata.getDomainType());
+ final RepositoryInformation repositoryInformation = getRepositoryInformation(metadata, null);
+ return new SimpleCouchbaseRepository(entityInformation, couchbaseOperations, repositoryInformation);
}
/**
* The base class for this repository.
*
* @param repositoryMetadata metadata for the repository.
+ *
* @return the base class.
*/
@Override
protected Class> getRepositoryBaseClass(final RepositoryMetadata repositoryMetadata) {
return SimpleCouchbaseRepository.class;
}
+
}
diff --git a/src/main/java/org/springframework/data/couchbase/repository/support/SimpleCouchbaseRepository.java b/src/main/java/org/springframework/data/couchbase/repository/support/SimpleCouchbaseRepository.java
index a1adefa43..f651286a0 100644
--- a/src/main/java/org/springframework/data/couchbase/repository/support/SimpleCouchbaseRepository.java
+++ b/src/main/java/org/springframework/data/couchbase/repository/support/SimpleCouchbaseRepository.java
@@ -21,11 +21,15 @@
import com.couchbase.client.protocol.views.ViewResponse;
import com.couchbase.client.protocol.views.ViewRow;
import org.springframework.data.couchbase.core.CouchbaseOperations;
+import org.springframework.data.couchbase.core.view.View;
import org.springframework.data.couchbase.repository.CouchbaseRepository;
import org.springframework.data.couchbase.repository.query.CouchbaseEntityInformation;
+import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.util.Assert;
+import org.springframework.util.ReflectionUtils;
import java.io.Serializable;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@@ -46,6 +50,15 @@ public class SimpleCouchbaseRepository implements Co
*/
private final CouchbaseEntityInformation entityInformation;
+ /**
+ * Contains information about this repository.
+ */
+ private final RepositoryInformation repositoryInformation;
+
+ /**
+ * Convenience to hold all declared methods on the interface since we don't want to iterate every time!
+ */
+ private final Method[] allDeclaredMethods;
/**
* Create a new Repository.
@@ -54,14 +67,19 @@ public class SimpleCouchbaseRepository implements Co
* @param couchbaseOperations the reference to the template used.
*/
public SimpleCouchbaseRepository(final CouchbaseEntityInformation metadata,
- final CouchbaseOperations couchbaseOperations) {
+ final CouchbaseOperations couchbaseOperations,
+ final RepositoryInformation repositoryInformation) {
Assert.notNull(couchbaseOperations);
Assert.notNull(metadata);
+ Assert.notNull(repositoryInformation);
entityInformation = metadata;
this.couchbaseOperations = couchbaseOperations;
+ this.repositoryInformation = repositoryInformation;
+ allDeclaredMethods = ReflectionUtils.getAllDeclaredMethods(repositoryInformation.getRepositoryInterface());
}
+
@Override
public S save(S entity) {
Assert.notNull(entity, "Entity must not be null!");
@@ -75,7 +93,7 @@ public Iterable save(Iterable entities) {
Assert.notNull(entities, "The given Iterable of entities must not be null!");
List result = new ArrayList();
- for(S entity : entities) {
+ for (S entity : entities) {
save(entity);
result.add(entity);
}
@@ -109,7 +127,7 @@ public void delete(T entity) {
@Override
public void delete(Iterable extends T> entities) {
Assert.notNull(entities, "The given Iterable of entities must not be null!");
- for (T entity: entities) {
+ for (T entity : entities) {
couchbaseOperations.remove(entity);
}
}
@@ -117,37 +135,60 @@ public void delete(Iterable extends T> entities) {
@Override
public Iterable findAll() {
String design = entityInformation.getJavaType().getSimpleName().toLowerCase();
- String view = "all";
+ String viewName = "all";
- return couchbaseOperations.findByView(design, view, new Query().setReduce(false),
- entityInformation.getJavaType());
+ final Method findAllMethod = repositoryInformation.getCrudMethods().getFindAllMethod();
+ final View view = findAllMethod.getAnnotation(View.class);
+ if (view != null) {
+ design = view.design();
+ viewName = view.view();
+ }
+
+ return couchbaseOperations.findByView(design, viewName, new Query().setReduce(false), entityInformation.getJavaType());
}
@Override
public Iterable findAll(final Iterable ids) {
String design = entityInformation.getJavaType().getSimpleName().toLowerCase();
- String view = "all";
+ String viewName = "all";
+
+ final Method findAllMethod = repositoryInformation.getCrudMethods().getFindAllMethod();
+ final View view = findAllMethod.getAnnotation(View.class);
+ if (view != null) {
+ design = view.design();
+ viewName = view.view();
+ }
Query query = new Query();
query.setReduce(false);
query.setKeys(ComplexKey.of(ids));
- return couchbaseOperations.findByView(design, view, query, entityInformation.getJavaType());
+ return couchbaseOperations.findByView(design, viewName, query, entityInformation.getJavaType());
}
@Override
public long count() {
String design = entityInformation.getJavaType().getSimpleName().toLowerCase();
- String view = "all";
+ String viewName = "all";
+
+ for (final Method method : allDeclaredMethods) {
+ if (method.getName().equals("count")) {
+ final View view = method.getAnnotation(View.class);
+ if (view != null) {
+ design = view.design();
+ viewName = view.view();
+ }
+ }
+ }
Query query = new Query();
query.setReduce(true);
- ViewResponse response = couchbaseOperations.queryView(design, view, query);
+ ViewResponse response = couchbaseOperations.queryView(design, viewName, query);
long count = 0;
for (ViewRow row : response) {
- count += Long.parseLong(row.getValue());
+ count += Long.parseLong(row.getValue());
}
return count;
@@ -156,12 +197,19 @@ public long count() {
@Override
public void deleteAll() {
String design = entityInformation.getJavaType().getSimpleName().toLowerCase();
- String view = "all";
+ String viewName = "all";
+
+ final Method deleteMethod = repositoryInformation.getCrudMethods().getDeleteMethod();
+ final View view = deleteMethod.getAnnotation(View.class);
+ if (view != null) {
+ design = view.design();
+ viewName = view.view();
+ }
Query query = new Query();
query.setReduce(false);
- ViewResponse response = couchbaseOperations.queryView(design, view, query);
+ ViewResponse response = couchbaseOperations.queryView(design, viewName, query);
for (ViewRow row : response) {
couchbaseOperations.remove(row.getId());
}
diff --git a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryViewListener.java b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryViewListener.java
index 2a8601581..26e62590d 100644
--- a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryViewListener.java
+++ b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryViewListener.java
@@ -23,9 +23,6 @@
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
-/**
- * @author Michael Nitschinger
- */
public class CouchbaseRepositoryViewListener extends DependencyInjectionTestExecutionListener {
@Override
@@ -35,20 +32,18 @@ public void beforeTestClass(final TestContext testContext) throws Exception {
createAndWaitForDesignDocs(client);
}
- private void populateTestData(CouchbaseClient client) {
+ private void populateTestData(final CouchbaseClient client) {
CouchbaseTemplate template = new CouchbaseTemplate(client);
-
- for(int i=0;i < 100; i++) {
- User u = new User("testuser-" + i, "uname" + i);
- template.save(u);
+ for (int i = 0; i < 100; i++) {
+ template.save(new User("testuser-" + i, "uname-" + i));
}
}
- private void createAndWaitForDesignDocs(CouchbaseClient client) {
- DesignDocument designDoc = new DesignDocument("user");
- String mapFunction = "function (doc, meta) { if(doc._class == "
- + "\"org.springframework.data.couchbase.repository.User\") { emit(null, null); } }";
- designDoc.setView(new ViewDesign("all", mapFunction, "_count"));
+ private void createAndWaitForDesignDocs(final CouchbaseClient client) {
+ final DesignDocument designDoc = new DesignDocument("user");
+ final String mapFunction = "function (doc, meta) { if(doc._class == \"org.springframework.data.couchbase.repository.User\") { emit(null, null); } }";
+ designDoc.setView(new ViewDesign("customFindAllView", mapFunction, "_count"));
+ designDoc.setView(new ViewDesign("customCountView", mapFunction, "_count"));
client.createDesignDoc(designDoc);
}
diff --git a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryViewTests.java b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryViewTests.java
new file mode 100644
index 000000000..1d8cb5298
--- /dev/null
+++ b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryViewTests.java
@@ -0,0 +1,58 @@
+package org.springframework.data.couchbase.repository;
+
+import com.couchbase.client.CouchbaseClient;
+import com.couchbase.client.protocol.views.Query;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.couchbase.TestApplicationConfig;
+import org.springframework.data.couchbase.core.CouchbaseTemplate;
+import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactory;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.TestExecutionListeners;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import java.util.Iterator;
+
+import static com.couchbase.client.protocol.views.Stale.FALSE;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(classes = TestApplicationConfig.class)
+@TestExecutionListeners(CouchbaseRepositoryViewListener.class)
+public class CouchbaseRepositoryViewTests {
+
+ @Autowired
+ private CouchbaseClient client;
+
+ @Autowired
+ private CouchbaseTemplate template;
+
+ private UserRepositoryCustom repository;
+
+ @Before
+ public void setup() throws Exception {
+ repository = new CouchbaseRepositoryFactory(template).getRepository(UserRepositoryCustom.class);
+ }
+
+ @Test
+ public void shouldFindAllWithCustomView() {
+ client.query(client.getView("user", "customFindAllView"), new Query().setStale(FALSE));
+ Iterable allUsers = repository.findAll();
+ int i = 0;
+ for (final User allUser : allUsers) {
+ i++;
+ }
+ assertThat(i, is(100));
+ }
+
+ @Test
+ public void shouldCountWithCustomView() {
+ client.query(client.getView("user", "customCountView"), new Query().setStale(FALSE));
+ final long value = repository.count();
+ assertThat(value, is(100L));
+ }
+
+}
diff --git a/src/test/java/org/springframework/data/couchbase/repository/SimpleCouchbaseRepositoryListener.java b/src/test/java/org/springframework/data/couchbase/repository/SimpleCouchbaseRepositoryListener.java
new file mode 100644
index 000000000..3a4377ff2
--- /dev/null
+++ b/src/test/java/org/springframework/data/couchbase/repository/SimpleCouchbaseRepositoryListener.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.data.couchbase.repository;
+
+import com.couchbase.client.CouchbaseClient;
+import com.couchbase.client.protocol.views.DesignDocument;
+import com.couchbase.client.protocol.views.ViewDesign;
+import org.springframework.data.couchbase.core.CouchbaseTemplate;
+import org.springframework.test.context.TestContext;
+import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
+
+/**
+ * @author Michael Nitschinger
+ */
+public class SimpleCouchbaseRepositoryListener extends DependencyInjectionTestExecutionListener {
+
+ @Override
+ public void beforeTestClass(final TestContext testContext) throws Exception {
+ CouchbaseClient client = (CouchbaseClient) testContext.getApplicationContext().getBean("couchbaseClient");
+ populateTestData(client);
+ createAndWaitForDesignDocs(client);
+ }
+
+ private void populateTestData(CouchbaseClient client) {
+ CouchbaseTemplate template = new CouchbaseTemplate(client);
+
+ for (int i = 0; i < 100; i++) {
+ User u = new User("testuser-" + i, "uname-" + i);
+ template.save(u);
+ }
+ }
+
+ private void createAndWaitForDesignDocs(CouchbaseClient client) {
+ DesignDocument designDoc = new DesignDocument("user");
+ String mapFunction = "function (doc, meta) { if(doc._class == \"org.springframework.data.couchbase.repository.User\") { emit(null, null); } }";
+ designDoc.setView(new ViewDesign("all", mapFunction, "_count"));
+ client.createDesignDoc(designDoc);
+ }
+
+}
diff --git a/src/test/java/org/springframework/data/couchbase/repository/SimpleCouchbaseRepositoryTests.java b/src/test/java/org/springframework/data/couchbase/repository/SimpleCouchbaseRepositoryTests.java
index 90883dae2..d7101206d 100644
--- a/src/test/java/org/springframework/data/couchbase/repository/SimpleCouchbaseRepositoryTests.java
+++ b/src/test/java/org/springframework/data/couchbase/repository/SimpleCouchbaseRepositoryTests.java
@@ -38,14 +38,12 @@
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestApplicationConfig.class)
-@TestExecutionListeners(CouchbaseRepositoryViewListener.class)
+@TestExecutionListeners(SimpleCouchbaseRepositoryListener.class)
public class SimpleCouchbaseRepositoryTests {
-
@Autowired
private CouchbaseClient client;
-
@Autowired
private CouchbaseTemplate template;
@@ -75,6 +73,9 @@ public void simpleCrud() {
}
@Test
+ /**
+ * This test uses/assumes a default view called "all" that is configured on Couchbase.
+ */
public void shouldFindAll() {
// do a non-stale query to populate data for testing.
client.query(client.getView("user", "all"), new Query().setStale(Stale.FALSE));
diff --git a/src/test/java/org/springframework/data/couchbase/repository/UserRepository.java b/src/test/java/org/springframework/data/couchbase/repository/UserRepository.java
index dbbf55a3e..da78c6f76 100644
--- a/src/test/java/org/springframework/data/couchbase/repository/UserRepository.java
+++ b/src/test/java/org/springframework/data/couchbase/repository/UserRepository.java
@@ -19,6 +19,6 @@
/**
* @author Michael Nitschinger
*/
-public interface UserRepository extends CouchbaseRepository{
+public interface UserRepository extends CouchbaseRepository {
}
diff --git a/src/test/java/org/springframework/data/couchbase/repository/UserRepositoryCustom.java b/src/test/java/org/springframework/data/couchbase/repository/UserRepositoryCustom.java
new file mode 100644
index 000000000..ca417a651
--- /dev/null
+++ b/src/test/java/org/springframework/data/couchbase/repository/UserRepositoryCustom.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.data.couchbase.repository;
+
+import org.springframework.data.couchbase.core.view.View;
+
+public interface UserRepositoryCustom extends CouchbaseRepository {
+
+ @Override
+ @View(design = "user", view = "customFindAllView")
+ Iterable findAll();
+
+ @Override
+ @View(design = "user", view = "customCountView")
+ long count();
+
+}