DATACMNS-151 - Improve CrudRepository method signature generics.

Method signatures of save(…) methods in CrudRepository now return the concrete type passed in. This way subtypes can be persisted without casting the result back.

class Contact { … }
class Person extends Contact { … }

interface ContactRepository extends CrudRepository<Contact, Long> { … }

Before:

Person person = new Person();
person = (Person) repo.save(person);
         ^^^^^^^^

Now:

Person person = new Person();
person = repo.save(person);
This commit is contained in:
Oliver Gierke
2012-04-13 18:42:41 +02:00
parent c158442dc7
commit 67c8c53f5e
3 changed files with 39 additions and 8 deletions

View File

@@ -33,7 +33,7 @@ public interface CrudRepository<T, ID extends Serializable> extends Repository<T
* @param entity
* @return the saved entity
*/
T save(T entity);
<S extends T> S save(S entity);
/**
* Saves all given entities.
@@ -42,7 +42,7 @@ public interface CrudRepository<T, ID extends Serializable> extends Repository<T
* @return the saved entities
* @throws IllegalArgumentException in case the given entity is (@literal null}.
*/
Iterable<T> save(Iterable<? extends T> entities);
<S extends T> Iterable<S> save(Iterable<S> entities);
/**
* Retrives an entity by its id.

View File

@@ -15,8 +15,8 @@
*/
package org.springframework.data.repository.core.support;
import static org.springframework.data.repository.util.ClassUtils.*;
import static org.springframework.core.GenericTypeResolver.*;
import static org.springframework.data.repository.util.ClassUtils.*;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
@@ -261,8 +261,7 @@ class DefaultRepositoryInformation extends AbstractRepositoryMetadata implements
Class<?> parameterType = resolveParameterType(parameter, metadata.getRepositoryInterface());
if (type instanceof TypeVariable<?>) {
String name = ((TypeVariable<?>) type).getName();
if (!matchesGenericType(name, parameterType)) {
if (!matchesGenericType((TypeVariable<?>) type, parameterType)) {
return false;
}
} else {
@@ -284,16 +283,16 @@ class DefaultRepositoryInformation extends AbstractRepositoryMetadata implements
* @param parameterType
* @return
*/
private boolean matchesGenericType(String name, Class<?> parameterType) {
private boolean matchesGenericType(TypeVariable<?> variable, Class<?> parameterType) {
Class<?> entityType = getDomainClass();
Class<?> idClass = getIdClass();
if (ID_TYPE_NAME.equals(name) && parameterType.equals(idClass)) {
if (ID_TYPE_NAME.equals(variable.getName()) && parameterType.isAssignableFrom(idClass)) {
return true;
}
if (DOMAIN_TYPE_NAME.equals(name) && parameterType.equals(entityType)) {
if (DOMAIN_TYPE_NAME.equals(variable.getBounds()[0].toString()) && parameterType.isAssignableFrom(entityType)) {
return true;
}

View File

@@ -8,6 +8,8 @@ import java.lang.reflect.Method;
import java.util.List;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
import org.hamcrest.collection.IsEmptyIterable;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -96,6 +98,33 @@ public class DefaultRepositoryInformationUnitTests {
assertThat(information.getQueryMethods(), is(empty));
}
/**
* @see DATACMNS-151
*/
@Test
public void doesNotConsiderManuallyDefinedSaveMethodAQueryMethod() {
RepositoryMetadata metadata = new DefaultRepositoryMetadata(CustomRepository.class);
RepositoryInformation information = new DefaultRepositoryInformation(metadata, PagingAndSortingRepository.class,
null);
assertThat(information.getQueryMethods(), is(IsEmptyIterable.<Method> emptyIterable()));
}
/**
* @see DATACMNS-151
*/
@Test
public void doesNotConsiderRedeclaredSaveMethodAQueryMethod() throws Exception {
RepositoryMetadata metadata = new DefaultRepositoryMetadata(ConcreteRepository.class);
RepositoryInformation information = new DefaultRepositoryInformation(metadata, CrudRepository.class, null);
Method saveMethod = BaseRepository.class.getMethod("save", Object.class);
assertThat(information.getQueryMethods(), is(Matchers.<Method> iterableWithSize(2)));
assertThat(information.getQueryMethods(), not(hasItem(saveMethod)));
}
private Method getMethodFrom(Class<?> type, String name) {
for (Method method : type.getMethods()) {
if (method.getName().equals(name)) {
@@ -133,6 +162,8 @@ public class DefaultRepositoryInformationUnitTests {
interface BaseRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
T findBySomething(String something);
<K extends T> K save(K entity);
}
interface ConcreteRepository extends BaseRepository<User, Integer> {
@@ -157,5 +188,6 @@ public class DefaultRepositoryInformationUnitTests {
interface CustomRepository extends ReadOnlyRepository<Object, Long> {
Object save(Object object);
}
}