diff --git a/Spring Data Commons.sonargraph b/Spring Data Commons.sonargraph
index 881e12d26..54f37a727 100644
--- a/Spring Data Commons.sonargraph
+++ b/Spring Data Commons.sonargraph
@@ -80,6 +80,7 @@
+
diff --git a/src/main/java/org/springframework/data/repository/core/CrudInvoker.java b/src/main/java/org/springframework/data/repository/core/CrudInvoker.java
new file mode 100644
index 000000000..cffaf14a8
--- /dev/null
+++ b/src/main/java/org/springframework/data/repository/core/CrudInvoker.java
@@ -0,0 +1,46 @@
+/*
+ * 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.repository.core;
+
+import java.io.Serializable;
+
+import org.springframework.data.repository.CrudRepository;
+
+/**
+ * Interface for components that can invoke simple CRUD operations on repositories. Useful to be able to abstract being
+ * backed by a {@link CrudRepository} implementation or a raw repository declaration with signature compatible methods
+ * for {@link CrudRepository#findOne(Serializable)} and {@link CrudRepository#save(Object)}.
+ *
+ * @author Oliver Gierke
+ * @since 1.6
+ */
+public interface CrudInvoker {
+
+ /**
+ * Invokes the method equivalent to {@link CrudRepository#save(Object)}.
+ *
+ * @param object must not be {@literal null}.
+ */
+ T invokeSave(T object);
+
+ /**
+ * Invokes the method equivalent to {@link CrudRepository#findOne(Serializable)}.
+ *
+ * @param id must not be {@literal null}.
+ * @return
+ */
+ T invokeFindOne(Serializable id);
+}
diff --git a/src/main/java/org/springframework/data/repository/core/CrudMethods.java b/src/main/java/org/springframework/data/repository/core/CrudMethods.java
new file mode 100644
index 000000000..8a87dbad0
--- /dev/null
+++ b/src/main/java/org/springframework/data/repository/core/CrudMethods.java
@@ -0,0 +1,95 @@
+/*
+ * 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.repository.core;
+
+import java.lang.reflect.Method;
+
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.PagingAndSortingRepository;
+
+/**
+ * Meta-information about the CRUD methods of a repository.
+ *
+ * @author Oliver Gierke
+ * @since 1.6
+ */
+public interface CrudMethods {
+
+ /**
+ * Returns the method to be used for saving entities. Usually siganture compatible to
+ * {@link CrudRepository#save(Object)}.
+ *
+ * @return the method to save entities or {@literal null} if noen exposed.
+ * @see #hasSaveMethod()
+ */
+ Method getSaveMethod();
+
+ /**
+ * Returns whether the repository exposes a save method at all.
+ *
+ * @return
+ */
+ boolean hasSaveMethod();
+
+ /**
+ * Returns the find all method of the repository. Implementations should prefer more detailled methods like
+ * {@link PagingAndSortingRepository}'s taking a {@link Pageable} or {@link Sort} instance.
+ *
+ * @return the find all method of the repository or {@literal null} if not available.
+ * @see #hasFindAllMethod()
+ */
+ Method getFindAllMethod();
+
+ /**
+ * Returns whether the repository exposes a find all method at all.
+ *
+ * @return
+ */
+ boolean hasFindAllMethod();
+
+ /**
+ * Returns the find one method of the repository. Usually signature compatible to
+ * {@link CrudRepository#findOne(java.io.Serializable)}
+ *
+ * @return the find one method of the repository or {@literal null} if not available.
+ * @see #hasFindOneMethod()
+ */
+ Method getFindOneMethod();
+
+ /**
+ * Returns whether the repository exposes a find one method.
+ *
+ * @return
+ */
+ boolean hasFindOneMethod();
+
+ /**
+ * Returns the delete method of the repository. Will prefer a delete-by-id method over a delete-by-entity method.
+ *
+ * @return the delete method of the repository or {@literal null} if not available.
+ * @see #hasDelete()
+ */
+ Method getDeleteMethod();
+
+ /**
+ * Returns whether the repository esposes a delete method.
+ *
+ * @return
+ */
+ boolean hasDelete();
+}
diff --git a/src/main/java/org/springframework/data/repository/core/RepositoryInformation.java b/src/main/java/org/springframework/data/repository/core/RepositoryInformation.java
index 0817102e1..8594fd756 100644
--- a/src/main/java/org/springframework/data/repository/core/RepositoryInformation.java
+++ b/src/main/java/org/springframework/data/repository/core/RepositoryInformation.java
@@ -55,6 +55,15 @@ public interface RepositoryInformation extends RepositoryMetadata {
*/
boolean isQueryMethod(Method method);
+ /**
+ * Returns whether the given method is logically a base class method. This also includes methods (re)declared in the
+ * repository interface that match the signatures of the base implementation.
+ *
+ * @param method must not be {@literal null}.
+ * @return
+ */
+ boolean isBaseClassMethod(Method method);
+
/**
* Returns all methods considered to be query methods.
*
@@ -62,6 +71,13 @@ public interface RepositoryInformation extends RepositoryMetadata {
*/
Iterable getQueryMethods();
+ /**
+ * Returns {@link CrudMethods} meta information for the repository.
+ *
+ * @return
+ */
+ CrudMethods getCrudMethods();
+
/**
* Returns the target class method that is backing the given method. This can be necessary if a repository interface
* redeclares a method of the core repository interface (e.g. for transaction behaviour customization). Returns the
diff --git a/src/main/java/org/springframework/data/repository/core/support/DefaultCrudMethods.java b/src/main/java/org/springframework/data/repository/core/support/DefaultCrudMethods.java
new file mode 100644
index 000000000..22305a281
--- /dev/null
+++ b/src/main/java/org/springframework/data/repository/core/support/DefaultCrudMethods.java
@@ -0,0 +1,201 @@
+/*
+ * 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.repository.core.support;
+
+import java.lang.reflect.Method;
+
+import org.springframework.core.GenericTypeResolver;
+import org.springframework.core.MethodParameter;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.core.CrudMethods;
+import org.springframework.data.repository.core.RepositoryInformation;
+import org.springframework.util.Assert;
+import org.springframework.util.ReflectionUtils;
+
+/**
+ * Default implementation to discover CRUD methods based on a given {@link RepositoryInformation}. Will detect methods
+ * exposed in {@link CrudRepository} but also hand crafted CRUD methods that are signature compatible with the ones on
+ * {@link CrudRepository}.
+ *
+ * @author Oliver Gierke
+ * @since 1.6
+ */
+class DefaultCrudMethods implements CrudMethods {
+
+ private final RepositoryInformation information;
+
+ private Method findAllMethod;
+ private boolean findAllHasPaging;
+
+ private Method findOneMethod;
+ private Method saveMethod;
+ private Method deleteMethod;
+
+ /**
+ * Creates a new {@link DefaultCrudMethods} using the given {@link RepositoryInformation}.
+ *
+ * @param information must not be {@literal null}.
+ */
+ public DefaultCrudMethods(RepositoryInformation information) {
+
+ Assert.notNull(information, "RepositoryInformation must not be null!");
+ this.information = information;
+
+ for (Method method : ReflectionUtils.getAllDeclaredMethods(information.getRepositoryInterface())) {
+
+ if (!information.isBaseClassMethod(method)) {
+ continue;
+ }
+
+ if (method.getName().equals("findAll")) {
+ findAllDetected(method);
+ continue;
+ }
+
+ if (method.getName().equals("findOne")) {
+ this.findOneMethod = method;
+ }
+
+ if (method.getName().equals("save")) {
+ this.saveMethod = method;
+ }
+
+ if (method.getName().equals("delete")) {
+ deleteDetected(method);
+ }
+ }
+ }
+
+ /**
+ * Checks whether the given method is a more usable find all method. Will prefer methods taking a {@link Pageable} and
+ * sort over a simple one.
+ *
+ * @param method
+ */
+ private void findAllDetected(Method method) {
+
+ if (findAllMethod != null && findAllHasPaging) {
+ return;
+ }
+
+ Class>[] parameterType = method.getParameterTypes();
+
+ if (parameterType.length > 0) {
+
+ if (parameterType[0].equals(Pageable.class)) {
+ this.findAllMethod = method;
+ this.findAllHasPaging = true;
+ return;
+ }
+
+ if (parameterType[0].equals(Sort.class)) {
+ this.findAllMethod = method;
+ }
+
+ return;
+ }
+
+ if (findAllMethod == null) {
+ this.findAllMethod = method;
+ }
+ }
+
+ /**
+ * Checks whether the given method is a more usable delete method. Will prefer delete-by-id methods over
+ * delete-by-instance ones.
+ *
+ * @param method
+ */
+ private void deleteDetected(Method method) {
+
+ MethodParameter parameter = new MethodParameter(method, 0);
+ Class> parameterType = GenericTypeResolver.resolveParameterType(parameter, information.getRepositoryInterface());
+
+ if (information.getIdType().isAssignableFrom(parameterType)) {
+ this.deleteMethod = method;
+ }
+
+ if (this.deleteMethod == null) {
+ this.deleteMethod = method;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.springframework.data.repository.core.support.CrudMethods#getSaveMethod()
+ */
+ @Override
+ public Method getSaveMethod() {
+ return saveMethod;
+ }
+
+ /* (non-Javadoc)
+ * @see org.springframework.data.repository.core.support.CrudMethods#hasSaveMethod()
+ */
+ @Override
+ public boolean hasSaveMethod() {
+ return saveMethod != null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.springframework.data.repository.core.support.CrudMethods#getFindAllMethod()
+ */
+ @Override
+ public Method getFindAllMethod() {
+ return findAllMethod;
+ }
+
+ /* (non-Javadoc)
+ * @see org.springframework.data.repository.core.support.CrudMethods#hasFindAllMethod()
+ */
+ @Override
+ public boolean hasFindAllMethod() {
+ return findAllMethod != null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.springframework.data.repository.core.support.CrudMethods#getFindOneMethod()
+ */
+ @Override
+ public Method getFindOneMethod() {
+ return findOneMethod;
+ }
+
+ /* (non-Javadoc)
+ * @see org.springframework.data.repository.core.support.CrudMethods#hasFindOneMethod()
+ */
+ @Override
+ public boolean hasFindOneMethod() {
+ return findOneMethod != null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.springframework.data.repository.core.support.CrudMethods#hasDelete()
+ */
+ @Override
+ public boolean hasDelete() {
+ return this.deleteMethod != null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.springframework.data.repository.core.support.CrudMethods#getDeleteMethod()
+ */
+ @Override
+ public Method getDeleteMethod() {
+ return this.deleteMethod;
+ }
+}
diff --git a/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryInformation.java b/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryInformation.java
index 02dfb6aa2..d0e75ed4b 100644
--- a/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryInformation.java
+++ b/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryInformation.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2011-2012 the original author or authors.
+ * Copyright 2011-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.
@@ -30,6 +30,7 @@ import java.util.concurrent.ConcurrentHashMap;
import org.springframework.core.MethodParameter;
import org.springframework.data.repository.Repository;
+import org.springframework.data.repository.core.CrudMethods;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.util.Assert;
@@ -42,8 +43,8 @@ import org.springframework.util.ClassUtils;
*/
class DefaultRepositoryInformation extends AbstractRepositoryMetadata implements RepositoryInformation {
- @SuppressWarnings("rawtypes")
- private static final TypeVariable>[] PARAMETERS = Repository.class.getTypeParameters();
+ @SuppressWarnings("rawtypes") private static final TypeVariable>[] PARAMETERS = Repository.class
+ .getTypeParameters();
private static final String DOMAIN_TYPE_NAME = PARAMETERS[0].getName();
private static final String ID_TYPE_NAME = PARAMETERS[1].getName();
@@ -52,12 +53,13 @@ class DefaultRepositoryInformation extends AbstractRepositoryMetadata implements
private final RepositoryMetadata metadata;
private final Class> repositoryBaseClass;
private final Class> customImplementationClass;
+ private final CrudMethods crudMethods;
/**
* Creates a new {@link DefaultRepositoryMetadata} for the given repository interface and repository base class.
*
- * @param metadata
- * @param repositoryBaseClass
+ * @param metadata must not be {@literal null}.
+ * @param repositoryBaseClass must not be {@literal null}.
* @param customImplementationClass
*/
public DefaultRepositoryInformation(RepositoryMetadata metadata, Class> repositoryBaseClass,
@@ -71,12 +73,14 @@ class DefaultRepositoryInformation extends AbstractRepositoryMetadata implements
this.metadata = metadata;
this.repositoryBaseClass = repositoryBaseClass;
this.customImplementationClass = customImplementationClass;
+ this.crudMethods = new DefaultCrudMethods(this);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.support.RepositoryMetadata#getRepositoryInterface()
*/
+ @Override
public Class> getRepositoryInterface() {
return metadata.getRepositoryInterface();
}
@@ -85,6 +89,7 @@ class DefaultRepositoryInformation extends AbstractRepositoryMetadata implements
* (non-Javadoc)
* @see org.springframework.data.repository.support.RepositoryMetadata#getDomainClass()
*/
+ @Override
public Class> getDomainType() {
return metadata.getDomainType();
}
@@ -93,6 +98,7 @@ class DefaultRepositoryInformation extends AbstractRepositoryMetadata implements
* (non-Javadoc)
* @see org.springframework.data.repository.support.RepositoryMetadata#getIdClass()
*/
+ @Override
public Class extends Serializable> getIdType() {
return metadata.getIdType();
}
@@ -101,6 +107,7 @@ class DefaultRepositoryInformation extends AbstractRepositoryMetadata implements
* (non-Javadoc)
* @see org.springframework.data.repository.support.RepositoryInformation#getRepositoryBaseClass()
*/
+ @Override
public Class> getRepositoryBaseClass() {
return this.repositoryBaseClass;
}
@@ -109,6 +116,7 @@ class DefaultRepositoryInformation extends AbstractRepositoryMetadata implements
* (non-Javadoc)
* @see org.springframework.data.repository.support.RepositoryInformation#getTargetClassMethod(java.lang.reflect.Method)
*/
+ @Override
public Method getTargetClassMethod(Method method) {
if (methodCache.containsKey(method)) {
@@ -152,6 +160,7 @@ class DefaultRepositoryInformation extends AbstractRepositoryMetadata implements
* (non-Javadoc)
* @see org.springframework.data.repository.support.RepositoryInformation#getQueryMethods()
*/
+ @Override
public Set getQueryMethods() {
Set result = new HashSet();
@@ -170,6 +179,7 @@ class DefaultRepositoryInformation extends AbstractRepositoryMetadata implements
* (non-Javadoc)
* @see org.springframework.data.repository.support.RepositoryInformation#isCustomMethod(java.lang.reflect.Method)
*/
+ @Override
public boolean isCustomMethod(Method method) {
return isTargetClassMethod(method, customImplementationClass);
}
@@ -178,17 +188,19 @@ class DefaultRepositoryInformation extends AbstractRepositoryMetadata implements
* (non-Javadoc)
* @see org.springframework.data.repository.core.RepositoryInformation#isQueryMethod(java.lang.reflect.Method)
*/
+ @Override
public boolean isQueryMethod(Method method) {
return getQueryMethods().contains(method);
}
- /**
- * Returns whether the given method is a method covered by the base implementation.
- *
- * @param method
- * @return
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.repository.core.RepositoryInformation#isBaseClassMethod(java.lang.reflect.Method)
*/
+ @Override
public boolean isBaseClassMethod(Method method) {
+
+ Assert.notNull(method, "Method must not be null!");
return isTargetClassMethod(method, repositoryBaseClass);
}
@@ -234,6 +246,7 @@ class DefaultRepositoryInformation extends AbstractRepositoryMetadata implements
* (non-Javadoc)
* @see org.springframework.data.repository.support.RepositoryInformation#hasCustomMethod()
*/
+ @Override
public boolean hasCustomMethod() {
Class> repositoryInterface = getRepositoryInterface();
@@ -252,6 +265,15 @@ class DefaultRepositoryInformation extends AbstractRepositoryMetadata implements
return false;
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.repository.core.RepositoryInformation#getCrudMethods()
+ */
+ @Override
+ public CrudMethods getCrudMethods() {
+ return crudMethods;
+ }
+
/**
* Checks the given method's parameters to match the ones of the given base class method. Matches generic arguments
* agains the ones bound in the given repository interface.
diff --git a/src/main/java/org/springframework/data/repository/init/ResourceReaderRepositoryPopulator.java b/src/main/java/org/springframework/data/repository/init/ResourceReaderRepositoryPopulator.java
index 1489447eb..f95a85d3f 100644
--- a/src/main/java/org/springframework/data/repository/init/ResourceReaderRepositoryPopulator.java
+++ b/src/main/java/org/springframework/data/repository/init/ResourceReaderRepositoryPopulator.java
@@ -16,7 +16,6 @@
package org.springframework.data.repository.init;
import java.io.IOException;
-import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
@@ -27,7 +26,7 @@ import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
-import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.core.CrudInvoker;
import org.springframework.data.repository.support.Repositories;
import org.springframework.util.Assert;
@@ -152,10 +151,12 @@ public class ResourceReaderRepositoryPopulator implements RepositoryPopulator, A
* @param object must not be {@literal null}.
* @param repositories must not be {@literal null}.
*/
+ @SuppressWarnings({ "unchecked" })
private void persist(Object object, Repositories repositories) {
- CrudRepository