diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/MapResult.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/MapResult.java index 053215158..97177e825 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/MapResult.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/MapResult.java @@ -22,6 +22,10 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.TYPE}) +@Deprecated +/** + * @deprecated replaced by {@link QueryResult} + */ public @interface MapResult { String value() default ""; } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/POJOResult.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/QueryResult.java similarity index 88% rename from spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/POJOResult.java rename to spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/QueryResult.java index d9fa780a7..478fe4b14 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/POJOResult.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/QueryResult.java @@ -21,7 +21,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Annotation to mark a POJO as being able to hold the results of a + * Annotation to mark either a POJO or interface as being able to hold the results of a * SDN based query. * * @author Nicki Watt @@ -29,6 +29,6 @@ import java.lang.annotation.Target; */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) -public @interface POJOResult { - String value() default ""; +public @interface QueryResult { + } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/conversion/EntityResultConverter.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/conversion/EntityResultConverter.java index ddecdf22a..2c0a5a91e 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/conversion/EntityResultConverter.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/conversion/EntityResultConverter.java @@ -21,7 +21,7 @@ import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; import org.springframework.core.convert.ConversionService; import org.springframework.data.neo4j.annotation.MapResult; -import org.springframework.data.neo4j.annotation.POJOResult; +import org.springframework.data.neo4j.annotation.QueryResult; import org.springframework.data.neo4j.annotation.ResultColumn; import org.springframework.data.neo4j.conversion.DefaultConverter; import org.springframework.data.neo4j.conversion.QueryResultBuilder; @@ -90,7 +90,7 @@ public class EntityResultConverter extends DefaultConverter implemen public R extractPOJOResult(Object value, Class returnType, MappingPolicy mappingPolicy) { String errorMessage = "Error extracting and setting value for POJO Result : " + returnType; if (!Map.class.isAssignableFrom(value.getClass())) { - throw new RuntimeException("POJOResult can only be extracted from Map."); + throw new RuntimeException("QueryResult can only be extracted from Map."); } Object newThing = null; @@ -145,7 +145,7 @@ public class EntityResultConverter extends DefaultConverter implemen } @SuppressWarnings("unchecked") - public R extractMapResult(Object value, Class returnType, MappingPolicy mappingPolicy) { + public R extractProxyBasedResult(Object value, Class returnType, MappingPolicy mappingPolicy) { if (!Map.class.isAssignableFrom(value.getClass())) { throw new RuntimeException("MapResult can only be extracted from Map."); } @@ -157,12 +157,23 @@ public class EntityResultConverter extends DefaultConverter implemen @Override public R convert(Object value, Class type, MappingPolicy mappingPolicy) { - if (type.isAnnotationPresent(MapResult.class)) { - return extractMapResult(value, type,mappingPolicy); - } else if (type.isAnnotationPresent(POJOResult.class)) { + if (isInterfaceBasedMappingRequest(type)) { + return extractProxyBasedResult(value, type, mappingPolicy); + } else if (isPojoBasedMappingReqest(type)) { return extractPOJOResult(value, type,mappingPolicy); } else return super.convert(value, type,mappingPolicy); } + boolean isInterfaceBasedMappingRequest(Class type) { + // MapResult is deprecated now but we still need to check for it + return type.isInterface() && + (type.isAnnotationPresent(MapResult.class) || + type.isAnnotationPresent(QueryResult.class)); + } + + boolean isPojoBasedMappingReqest(Class type) { + return !type.isInterface() && type.isAnnotationPresent(QueryResult.class); + } + } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/MemberData.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/MemberData.java index bd785a282..89f1ac1e8 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/MemberData.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/MemberData.java @@ -15,13 +15,14 @@ */ package org.springframework.data.neo4j.repository; -import org.springframework.data.neo4j.annotation.MapResult; +import org.springframework.data.neo4j.annotation.QueryResult; import org.springframework.data.neo4j.annotation.ResultColumn; import org.springframework.data.neo4j.model.Group; import org.springframework.data.neo4j.model.Person; -@MapResult +@QueryResult public interface MemberData { + @ResultColumn("collect(team)") Iterable getTeams(); diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/MemberDataPOJO.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/MemberDataPOJO.java index f0613a972..2afe91071 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/MemberDataPOJO.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/MemberDataPOJO.java @@ -15,7 +15,7 @@ */ package org.springframework.data.neo4j.repository; -import org.springframework.data.neo4j.annotation.POJOResult; +import org.springframework.data.neo4j.annotation.QueryResult; import org.springframework.data.neo4j.annotation.ResultColumn; import org.springframework.data.neo4j.model.Group; import org.springframework.data.neo4j.model.Person; @@ -24,7 +24,7 @@ import java.io.Serializable; import java.util.Set; -@POJOResult +@QueryResult public class MemberDataPOJO implements Serializable { private static final long serialVersionUID = 1L; diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/PersonRepository.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/PersonRepository.java index 599dcd524..f60434c60 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/PersonRepository.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/PersonRepository.java @@ -19,8 +19,8 @@ package org.springframework.data.neo4j.repository; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; -import org.springframework.data.neo4j.annotation.MapResult; import org.springframework.data.neo4j.annotation.Query; +import org.springframework.data.neo4j.annotation.QueryResult; import org.springframework.data.neo4j.annotation.QueryType; import org.springframework.data.neo4j.annotation.ResultColumn; import org.springframework.data.neo4j.conversion.EndResult; @@ -95,7 +95,7 @@ public interface PersonRepository extends GraphRepository, NamedIndexRep EndResult findByHeight( short height ); - @MapResult + @QueryResult interface NameAndPersonResult { @ResultColumn("name") diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/conversion/EntityResultConverterTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/conversion/EntityResultConverterTest.java new file mode 100644 index 000000000..8cae72503 --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/conversion/EntityResultConverterTest.java @@ -0,0 +1,96 @@ +package org.springframework.data.neo4j.support.conversion; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.data.neo4j.annotation.MapResult; +import org.springframework.data.neo4j.annotation.ResultColumn; +import org.springframework.data.neo4j.model.Group; +import org.springframework.data.neo4j.model.Person; +import org.springframework.data.neo4j.repository.MemberData; +import org.springframework.data.neo4j.repository.MemberDataPOJO; + + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Unit Tests for EntityResultConverter class. + * + * @author Nicki Watt + * @since 12.08.2013 + */ +public class EntityResultConverterTest { + + private EntityResultConverter converter; + + @Before + public void setup() { + converter = new EntityResultConverter(null); + } + + @Test + public void testInterfaceWithDeprecatedMapResultAnnotationIsIdentifiedAsNeedingInterfaceBasedMapping() { + boolean result = converter.isInterfaceBasedMappingRequest(ADeprecatedMapResultInterface.class); + assertTrue("Expect interfaces with deprecated @MapResult annotation to be identified correctly", result); + } + + @Test + public void testInterfaceWithQueryAnnotationIsIdentifiedAsNeedingInterfaceBasedMapping() { + boolean result = converter.isInterfaceBasedMappingRequest(MemberData.class); + assertTrue("Expect interfaces with new @QueryResult annotation to be identified correctly", result); + } + + @Test + public void testInterfaceWithNoAnnotationIsNotIdentifiedAsNeedingInterfaceBasedMapping() { + boolean result = converter.isInterfaceBasedMappingRequest(APlainInterface.class); + assertFalse("Expect interfaces with no @QueryResult or @MapResult annotation to be identified correctly", result); + } + + @Test + public void testPojoWithQueryAnnotationIsNotIdentifiedAsNeedingInterfaceBasedMapping() { + boolean testResult = converter.isInterfaceBasedMappingRequest(MemberDataPOJO.class); + assertFalse("POJO annotated class with @QueryResult should not be identified as requiring interface based mapping", testResult); + } + + @Test + public void testPojoWithQueryAnnotationIsIdentifiedAsNeedingPOJOBasedMapping() { + boolean testResult = converter.isPojoBasedMappingReqest(MemberDataPOJO.class); + assertTrue("POJO annotated class with @QueryResult should be identified as requiring POJO based mapping", testResult); + } + + @Test + public void testPojoWithDeprecatedIFAnnotationIsIdentifiedCorrectly() { + boolean isPojoResult = converter.isPojoBasedMappingReqest(AConfusedPOJO.class); + boolean isInterfaceResult = converter.isInterfaceBasedMappingRequest(AConfusedPOJO.class); + assertFalse("POJO using deprecated @MapResult interface annotation should not be identified as requiring POJO based mapping", isPojoResult); + assertFalse("POJO using deprecated @MapResult annotation should not be identified as requiring Interface based mapping", isInterfaceResult); + } + +} + +@MapResult +interface ADeprecatedMapResultInterface { + + @ResultColumn("collect(team)") + Iterable getTeams(); + + @ResultColumn("boss") + Person getBoss(); +} + +interface APlainInterface { + + Iterable getTeams(); + + Person getBoss(); +} + +@MapResult +class AConfusedPOJO { + + @ResultColumn("collect(team)") + private Iterable teams; + + @ResultColumn("boss") + private Person boss; +}