delegate;
- private final Neo4jPersistentProperty property;
- private final Neo4jTemplate ctx;
- private final FieldAccessor fieldAccessor;
+ private final transient Neo4jPersistentProperty property;
+ private final transient Neo4jTemplate ctx;
+ private final transient FieldAccessor fieldAccessor;
private final MappingPolicy mappingPolicy;
@SuppressWarnings("unchecked")
diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/ManagedPrefixedDynamicProperties.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/ManagedPrefixedDynamicProperties.java
index 75d90e10d..1f4ff30cc 100644
--- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/ManagedPrefixedDynamicProperties.java
+++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/ManagedPrefixedDynamicProperties.java
@@ -29,10 +29,13 @@ import java.util.Map;
* deleted.
*/
public class ManagedPrefixedDynamicProperties extends PrefixedDynamicProperties {
+
+ private static final long serialVersionUID = 1L;
+
private final Object entity;
- private final Neo4jTemplate template;
- private final FieldAccessor fieldAccessor;
- private final Neo4jPersistentProperty property;
+ private transient final Neo4jTemplate template;
+ private transient final FieldAccessor fieldAccessor;
+ private transient final Neo4jPersistentProperty property;
private boolean isNode;
private MappingPolicy mappingPolicy;
diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/PrefixedDynamicProperties.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/PrefixedDynamicProperties.java
index 19d9638a0..fea4b1edd 100644
--- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/PrefixedDynamicProperties.java
+++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/PrefixedDynamicProperties.java
@@ -15,6 +15,7 @@
*/
package org.springframework.data.neo4j.fieldaccess;
+import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@@ -27,7 +28,10 @@ import java.util.Set;
*
* The methods *PrefixedProperty() allow to access the prefixed property key/values pairs directly.
*/
-public class PrefixedDynamicProperties implements DynamicProperties {
+public class PrefixedDynamicProperties implements DynamicProperties , Serializable {
+
+ private static final long serialVersionUID = 1L;
+
private final Map map;
protected final String prefix;
diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/MappingPolicy.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/MappingPolicy.java
index 3c645f670..622c8b0eb 100644
--- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/MappingPolicy.java
+++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/MappingPolicy.java
@@ -15,6 +15,7 @@
*/
package org.springframework.data.neo4j.mapping;
+import java.io.Serializable;
import java.util.*;
import static java.util.Arrays.asList;
@@ -32,7 +33,10 @@ public interface MappingPolicy {
boolean shouldLoad();
MappingPolicy combineWith(MappingPolicy mappingPolicy);
- public class DefaultMappingPolicy implements MappingPolicy {
+ public class DefaultMappingPolicy implements MappingPolicy , Serializable {
+
+ private static final long serialVersionUID = 1L;
+
private Set options;
public DefaultMappingPolicy(Option... options) {
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 40fea0653..ddecdf22a 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
@@ -16,19 +16,24 @@
package org.springframework.data.neo4j.support.conversion;
+import org.neo4j.helpers.collection.IteratorUtil;
+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.ResultColumn;
import org.springframework.data.neo4j.conversion.DefaultConverter;
-import org.springframework.data.neo4j.conversion.ResultConverter;
+import org.springframework.data.neo4j.conversion.QueryResultBuilder;
import org.springframework.data.neo4j.core.EntityPath;
-import org.springframework.data.neo4j.mapping.EntityPersister;
import org.springframework.data.neo4j.mapping.MappingPolicy;
import org.springframework.data.neo4j.support.Neo4jTemplate;
import org.springframework.data.neo4j.support.Neo4jTemplateAware;
import org.springframework.data.neo4j.support.path.ConvertingEntityPath;
-import javax.inject.Provider;
+import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.Map;
@@ -80,6 +85,65 @@ public class EntityResultConverter extends DefaultConverter implemen
return result;
}
+
+ @SuppressWarnings("unchecked")
+ 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.");
+ }
+
+ Object newThing = null;
+ ResultColumnValueExtractor resultColumnValueExtractor = new ResultColumnValueExtractor((Map) value,mappingPolicy,this);
+ try {
+ newThing = returnType.newInstance();
+ BeanWrapper wrapper = new BeanWrapperImpl( newThing );
+ for (Field field: returnType.getDeclaredFields()) {
+ extractAndSetValueOfField(wrapper, field, resultColumnValueExtractor);
+ }
+ } catch (IllegalAccessException e1) {
+ throw new POJOResultBuildingException(errorMessage, e1);
+ } catch (InstantiationException e2) {
+ throw new POJOResultBuildingException(errorMessage, e2);
+ } catch (InvocationTargetException e3) {
+ throw new POJOResultBuildingException(errorMessage, e3);
+ } catch (NoSuchMethodException e4) {
+ throw new POJOResultBuildingException(errorMessage, e4);
+ } catch (ClassNotFoundException e5) {
+ throw new POJOResultBuildingException(errorMessage, e5);
+ }
+
+ return (R) newThing;
+ }
+
+ private void extractAndSetValueOfField(BeanWrapper wrapper, Field field,
+ ResultColumnValueExtractor resultColumnValueExtractor)
+ throws InvocationTargetException , NoSuchMethodException , ClassNotFoundException , IllegalAccessException{
+ if (!isPOJOMappableField(field))
+ return;
+
+ Object val = resultColumnValueExtractor.extractFromField(field);
+ if (val != null) {
+ if (val.getClass().getEnclosingClass() != null &&
+ val.getClass().getEnclosingClass().equals(QueryResultBuilder.class)) {
+ val = IteratorUtil.asCollection((Iterable) val);
+ }
+ wrapper.setPropertyValue( field.getName(), val );
+ }
+
+ }
+
+ /**
+ * At present, the only fields which can be mapped to a POJO are those
+ * annotated with the ResultColumn annotation
+ *
+ * @param field
+ * @return
+ */
+ private boolean isPOJOMappableField(Field field) {
+ return field.getAnnotation(ResultColumn.class) != null;
+ }
+
@SuppressWarnings("unchecked")
public R extractMapResult(Object value, Class returnType, MappingPolicy mappingPolicy) {
if (!Map.class.isAssignableFrom(value.getClass())) {
@@ -95,6 +159,8 @@ public class EntityResultConverter extends DefaultConverter implemen
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)) {
+ return extractPOJOResult(value, type,mappingPolicy);
} else
return super.convert(value, type,mappingPolicy);
}
diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/conversion/POJOResultBuildingException.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/conversion/POJOResultBuildingException.java
new file mode 100644
index 000000000..ae29e83b9
--- /dev/null
+++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/conversion/POJOResultBuildingException.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright 2011 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.neo4j.support.conversion;
+
+import org.springframework.data.mapping.model.MappingException;
+
+/**
+ * Exception which occurs whilst trying to build and map a POJO
+ * from a Query result.
+ *
+ * @author Nicki Watt
+ * @since 06.08.2013
+ */
+public class POJOResultBuildingException extends MappingException {
+
+ private static final long serialVersionUID = 1L;
+
+ public POJOResultBuildingException(String message, Throwable t) {
+ super( message , t );
+ }
+
+}
diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/conversion/QueryResultProxy.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/conversion/QueryResultProxy.java
index 5ab60d0cb..8f52f8e48 100644
--- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/conversion/QueryResultProxy.java
+++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/conversion/QueryResultProxy.java
@@ -15,15 +15,10 @@
*/
package org.springframework.data.neo4j.support.conversion;
-import org.springframework.data.neo4j.annotation.ResultColumn;
-import org.springframework.data.neo4j.conversion.QueryResultBuilder;
import org.springframework.data.neo4j.conversion.ResultConverter;
import org.springframework.data.neo4j.mapping.MappingPolicy;
-import org.springframework.data.util.ClassTypeInformation;
-import org.springframework.data.util.TypeInformation;
import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
@@ -36,11 +31,13 @@ public class QueryResultProxy implements InvocationHandler {
private final Map map;
private final MappingPolicy mappingPolicy;
private final ResultConverter converter;
+ private final ResultColumnValueExtractor resultColumnValueExtractor;
public QueryResultProxy(Map map, MappingPolicy mappingPolicy, ResultConverter converter) {
this.map = map;
this.mappingPolicy = mappingPolicy;
this.converter = converter;
+ this.resultColumnValueExtractor = new ResultColumnValueExtractor(map,mappingPolicy,converter);
}
@SuppressWarnings("unchecked")
@@ -51,57 +48,13 @@ public class QueryResultProxy implements InvocationHandler {
}
if (method.getName().equals("hashCode") && (params==null || params.length == 0)) {
- return map.hashCode();
+ return map.hashCode();
}
- ResultColumn column = method.getAnnotation(ResultColumn.class);
- TypeInformation> returnType = ClassTypeInformation.fromReturnTypeOf(method);
+ return resultColumnValueExtractor.extractFromMethod(method);
- String columnName = column.value();
- if(!map.containsKey( columnName )) {
- throw new NoSuchColumnFoundException( columnName );
- }
-
- Object columnValue = map.get( columnName );
- if(columnValue==null) return null;
-
- // If the returned value is a Scala iterable, transform it to a Java iterable first
- Class iterableLikeInterface = implementsInterface("scala.collection.Iterable", columnValue.getClass());
- if (iterableLikeInterface!=null) {
- columnValue = transformScalaIterableToJavaIterable(columnValue, iterableLikeInterface);
- }
-
- if (returnType.isCollectionLike())
- return new QueryResultBuilder((Iterable)columnValue, converter).to(returnType.getActualType().getType());
- else
- return converter.convert(columnValue, returnType.getType(), mappingPolicy);
}
- public Object transformScalaIterableToJavaIterable(Object scalaIterable, Class iterableLikeIface) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
- // This is equivalent to doing this:
- // JavaConversions.asJavaIterable(((IterableLike) columnValue).toIterable());
- Class> javaConversions = iterableLikeIface.getClassLoader().loadClass("scala.collection.JavaConversions");
- Method asJavaIterable = javaConversions.getMethod("asJavaIterable", iterableLikeIface);
- Iterable> javaIterable = (Iterable>) asJavaIterable.invoke(null, scalaIterable);
- return javaIterable;
- }
- private Class implementsInterface(String interfaceName, Class clazz) {
- if(clazz.getCanonicalName().equals(interfaceName)) return clazz;
-
- Class superclass = clazz.getSuperclass();
- if(superclass != null) {
- Class iface = implementsInterface(interfaceName, superclass);
- if (iface!= null) return iface;
- }
-
- for(Class iface : clazz.getInterfaces()) {
- Class superIface = implementsInterface(interfaceName, iface);
- if(superIface!=null)
- return superIface;
- }
-
- return null;
- }
private boolean equalsInternal(Object me, Object other) {
if (other == null) {
diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/conversion/ResultColumnValueExtractor.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/conversion/ResultColumnValueExtractor.java
new file mode 100644
index 000000000..02268d9ce
--- /dev/null
+++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/conversion/ResultColumnValueExtractor.java
@@ -0,0 +1,110 @@
+package org.springframework.data.neo4j.support.conversion;
+
+import org.springframework.data.neo4j.annotation.ResultColumn;
+import org.springframework.data.neo4j.conversion.QueryResultBuilder;
+import org.springframework.data.neo4j.conversion.ResultConverter;
+import org.springframework.data.neo4j.mapping.MappingPolicy;
+import org.springframework.data.util.ClassTypeInformation;
+import org.springframework.data.util.TypeInformation;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Map;
+
+/**
+ * Given a method or field annotated with the @ResultColumn, this class will
+ * extract and return the associated value from the underlying query result.
+ * Note: Quite a lot of the code originated from QueryResultProxy and
+ * was moved into here.
+ *
+ * @author Nicki Watt
+ * @since 06.08.2013
+ */
+public class ResultColumnValueExtractor {
+
+ private final Map map;
+ private final MappingPolicy mappingPolicy;
+ private final ResultConverter converter;
+
+ public ResultColumnValueExtractor(Map map, MappingPolicy mappingPolicy, ResultConverter converter) {
+ this.map = map;
+ this.mappingPolicy = mappingPolicy;
+ this.converter = converter;
+ }
+
+ public Object extractFromField(Field field) throws ClassNotFoundException,
+ NoSuchMethodException,
+ IllegalAccessException,
+ InvocationTargetException {
+ ResultColumn column = field.getAnnotation(ResultColumn.class);
+ TypeInformation> returnType = ClassTypeInformation.fromTypeOf(field);
+ return extractFromAccessibleObject(column,returnType);
+ }
+
+ public Object extractFromMethod(Method method) throws ClassNotFoundException,
+ NoSuchMethodException,
+ IllegalAccessException,
+ InvocationTargetException {
+ ResultColumn column = method.getAnnotation(ResultColumn.class);
+ TypeInformation> returnType = ClassTypeInformation.fromReturnTypeOf(method);
+ return extractFromAccessibleObject(column,returnType);
+ }
+
+ public Object extractFromAccessibleObject(ResultColumn column, TypeInformation> returnType)
+ throws ClassNotFoundException,
+ NoSuchMethodException,
+ IllegalAccessException,
+ InvocationTargetException {
+
+ String columnName = column.value();
+ if(!map.containsKey( columnName )) {
+ throw new NoSuchColumnFoundException( columnName );
+ }
+
+ Object columnValue = map.get(columnName);
+ if(columnValue==null) return null;
+
+ // If the returned value is a Scala iterable, transform it to a Java iterable first
+ Class iterableLikeInterface = implementsInterface("scala.collection.Iterable", columnValue.getClass());
+ if (iterableLikeInterface!=null) {
+ columnValue = transformScalaIterableToJavaIterable(columnValue, iterableLikeInterface);
+ }
+
+ if (returnType.isCollectionLike()) {
+ QueryResultBuilder qrb = new QueryResultBuilder((Iterable)columnValue, converter);
+ return qrb.to(returnType.getActualType().getType());
+ } else
+ return converter.convert(columnValue, returnType.getType(), mappingPolicy);
+ }
+
+
+ public Object transformScalaIterableToJavaIterable(Object scalaIterable, Class iterableLikeIface) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+ // This is equivalent to doing this:
+ // JavaConversions.asJavaIterable(((IterableLike) columnValue).toIterable());
+
+ Class> javaConversions = iterableLikeIface.getClassLoader().loadClass("scala.collection.JavaConversions");
+ Method asJavaIterable = javaConversions.getMethod("asJavaIterable", iterableLikeIface);
+ Iterable> javaIterable = (Iterable>) asJavaIterable.invoke(null, scalaIterable);
+ return javaIterable;
+ }
+
+ private Class implementsInterface(String interfaceName, Class clazz) {
+ if(clazz.getCanonicalName().equals(interfaceName)) return clazz;
+
+ Class superclass = clazz.getSuperclass();
+ if(superclass != null) {
+ Class iface = implementsInterface(interfaceName, superclass);
+ if (iface!= null) return iface;
+ }
+
+ for(Class iface : clazz.getInterfaces()) {
+ Class superIface = implementsInterface(interfaceName, iface);
+ if(superIface!=null)
+ return superIface;
+ }
+
+ return null;
+ }
+
+}
diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/model/BestFriend.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/model/BestFriend.java
index 6814b8aa3..28dacefb8 100644
--- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/model/BestFriend.java
+++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/model/BestFriend.java
@@ -25,10 +25,14 @@ import org.springframework.data.neo4j.annotation.RelationshipType;
import org.springframework.data.neo4j.annotation.StartNode;
import org.springframework.data.neo4j.fieldaccess.DynamicProperties;
+import java.io.Serializable;
import java.util.Date;
@RelationshipEntity(type = "BEST_FRIEND")
-public class BestFriend {
+public class BestFriend implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
@GraphId
private Long id;
diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/model/Friendship.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/model/Friendship.java
index 6b40a2e5f..b187ad85b 100644
--- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/model/Friendship.java
+++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/model/Friendship.java
@@ -20,10 +20,14 @@ package org.springframework.data.neo4j.model;
import org.springframework.data.neo4j.annotation.*;
import org.springframework.data.neo4j.fieldaccess.DynamicProperties;
+import java.io.Serializable;
import java.util.Date;
@RelationshipEntity(useShortNames = false)
-public class Friendship {
+public class Friendship implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
@GraphId
private Long id;
@@ -56,7 +60,6 @@ public class Friendship {
private Date firstMeetingDate;
private DynamicProperties personalProperties;
-
private transient String latestLocation;
public Friendship(Person start, Person end, String type) {
diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/model/Group.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/model/Group.java
index 29375ba75..a93c987dc 100644
--- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/model/Group.java
+++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/model/Group.java
@@ -36,6 +36,7 @@ import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
import org.springframework.data.neo4j.support.index.IndexType;
import org.springframework.util.ObjectUtils;
+import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
@@ -43,7 +44,9 @@ import java.util.Set;
@NodeEntity
@TypeAlias("g")
-public class Group implements IGroup {
+public class Group implements IGroup , Serializable {
+
+ private static final long serialVersionUID = 1L;
public final static String OTHER_NAME_INDEX = "other_name";
public static final String SEARCH_GROUPS_INDEX = "search_groups";
@@ -59,7 +62,7 @@ public class Group implements IGroup {
private Iterable readOnlyPersons;
@GraphTraversal(traversal = PeopleTraversalBuilder.class, params = "persons")
- private Iterable people;
+ transient private Iterable people;
@GraphProperty
@Indexed
diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/model/Person.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/model/Person.java
index 3469b9721..b4c59323b 100644
--- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/model/Person.java
+++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/model/Person.java
@@ -17,6 +17,7 @@
package org.springframework.data.neo4j.model;
import org.neo4j.graphdb.*;
+import org.springframework.data.annotation.Transient;
import org.springframework.data.neo4j.annotation.*;
import org.springframework.data.neo4j.fieldaccess.DynamicProperties;
import org.springframework.data.neo4j.support.index.IndexType;
@@ -24,11 +25,14 @@ import org.springframework.data.neo4j.support.index.IndexType;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.Size;
+import java.io.Serializable;
import java.util.*;
@NodeEntity
-public class Person implements Being {
+public class Person implements Being , Serializable {
+
+ private static final long serialVersionUID = 1L;
public static final String NAME_INDEX = "name-index";
public static final org.neo4j.graphdb.RelationshipType KNOWS = DynamicRelationshipType.withName("knows");
@@ -77,19 +81,23 @@ public class Person implements Being {
@Fetch
@RelatedToVia(type = "knows", elementClass = Friendship.class)
- private Iterable friendships;
+ private Iterable friendships;
@Query("start person=node({self}) match (person)<-[?:boss]-(boss) return boss")
- private Person bossByQuery;
+ transient private Person bossByQuery;
+ // NW - all queries should be transient
@Query("start person=node({self}) match (person)<-[?:boss]-(boss) return boss.name")
- private String bossName;
+ transient private String bossName;
+ // NW - all queries should be transient
@Query("start person=node({self}) match (person)<-[:persons]-(team)-[:persons]->(member) return member")
- private Iterable otherTeamMembers;
+ transient private Iterable otherTeamMembers;
+ // NW - all queries should be transient
@Query("start person=node({self}) match (person)<-[:persons]-(team)-[:persons]->(member) return member.name?, member.age?")
- private Iterable> otherTeamMemberData;
+ transient private Iterable> otherTeamMemberData;
+ // NW - all queries should be transient
@RelatedTo(elementClass = Group.class, type = "interface_test", direction = Direction.OUTGOING)
private Set groups;
diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/model/RootEntity.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/model/RootEntity.java
index 710914189..c1e05b759 100644
--- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/model/RootEntity.java
+++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/model/RootEntity.java
@@ -18,12 +18,17 @@ package org.springframework.data.neo4j.model;
import org.springframework.data.neo4j.annotation.GraphId;
import org.springframework.data.neo4j.annotation.NodeEntity;
+import java.io.Serializable;
+
/**
* @author mh
* @since 23.12.11
*/
@NodeEntity
-public class RootEntity {
+public class RootEntity implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
@GraphId Long id;
String rootName;
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
new file mode 100644
index 000000000..f0613a972
--- /dev/null
+++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/MemberDataPOJO.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright 2011 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.neo4j.repository;
+
+import org.springframework.data.neo4j.annotation.POJOResult;
+import org.springframework.data.neo4j.annotation.ResultColumn;
+import org.springframework.data.neo4j.model.Group;
+import org.springframework.data.neo4j.model.Person;
+
+import java.io.Serializable;
+import java.util.Set;
+
+
+@POJOResult
+public class MemberDataPOJO implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @ResultColumn("collect(team)")
+ private Set teams;
+
+ @ResultColumn("boss")
+ private Person boss;
+
+ @ResultColumn("someonesAge")
+ private int anInt;
+
+ @ResultColumn("someonesName")
+ private String aName;
+
+ public Set getTeams() {
+ return teams;
+ }
+
+ public void setTeams(Set teams) {
+ this.teams = teams;
+ }
+
+ public Person getBoss() {
+ return boss;
+ }
+
+ public void setBoss(Person boss) {
+ this.boss = boss;
+ }
+
+ public int getAnInt() {
+ return anInt;
+ }
+
+ public void setAnInt(int anInt) {
+ this.anInt = anInt;
+ }
+
+ public String getAName() {
+ return aName;
+ }
+
+ public void setAName(String name) {
+ this.aName = name;
+ }
+}
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 b95da535e..599dcd524 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
@@ -51,6 +51,9 @@ public interface PersonRepository extends GraphRepository, NamedIndexRep
@Query("start member=node({p_person}) match team-[:persons]->member<-[?:boss]-boss return collect(team), boss")
Iterable findMemberData(@Param("p_person") Person person);
+ @Query("start member=node({p_person}) match team-[:persons]->member<-[?:boss]-boss return collect(team), boss, boss.name as someonesName, boss.age as someonesAge ")
+ MemberDataPOJO findMemberDataPojo(@Param("p_person") Person person);
+
@Query("start member=node({p_person}) match team-[:persons]->member<-[?:boss]-boss return member")
Iterable nonWorkingQuery(@Param("p_person") Person person);
diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/SerialTesters.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/SerialTesters.java
new file mode 100644
index 000000000..360216181
--- /dev/null
+++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/SerialTesters.java
@@ -0,0 +1,105 @@
+/**
+ * Copyright 2011 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.neo4j.repository;
+
+import org.neo4j.helpers.collection.MapUtil;
+import org.springframework.data.neo4j.fieldaccess.DynamicPropertiesContainer;
+import org.springframework.data.neo4j.model.Friendship;
+import org.springframework.data.neo4j.model.Group;
+import org.springframework.data.neo4j.model.Person;
+import org.springframework.data.neo4j.model.Personality;
+import org.springframework.data.neo4j.template.Neo4jOperations;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Map;
+
+import static java.util.Arrays.asList;
+
+/**
+ * Creates a whole bunch of SDN entities for testing various aspects of the
+ * Serialization process.
+ *
+ * @author Nicki Watt
+ * @since 06.08.2013
+ */
+public class SerialTesters {
+
+ public SimpleDateFormat bdayFormatter;
+
+ public Person michael;
+ public Person emil;
+ public Person david;
+ public Person tareq;
+ public Person nicki;
+ public Group serialTesterGroup;
+ public Friendship friendShip;
+ public Friendship friendShip2;
+ public Friendship friendShip3;
+
+ public SerialTesters createUpgraderTeam(GraphRepository repo, GraphRepository groupRepo, GraphRepository friendshipRepository)
+ {
+ emil = new Person("Emil", 30);
+
+ michael = new Person("Michael", 36);
+ michael.setBoss(emil);
+ michael.setPersonality(Personality.EXTROVERT);
+ michael.setLocation( "POINT(16 56)" );
+
+ david = new Person("David", 25);
+ david.setBoss(emil);
+ david.setLocation( 16.5, 56.5 );
+
+ tareq = new Person("Tareq", 36);
+ nicki = new Person("Nicki", 36);
+ bdayFormatter = new SimpleDateFormat("dd MMM yyyy HH:mm:ss");
+ try {
+ nicki.setBirthdate(bdayFormatter.parse("01 JAN 2013 00:00:00"));
+ } catch (ParseException e) {
+ throw new RuntimeException("Could not parse date ...");
+ }
+ nicki.setBoss(tareq);
+ nicki.setDynamicProperty("What is this???");
+ nicki.setHeight((short)100);
+ nicki.setLocation(51.5, 0.1);
+ nicki.setPersonalProperties(new DynamicPropertiesContainer());
+ nicki.setProperty("addressLine1","Somewhere");
+ nicki.setProperty("addressLine2","Over the rainbow");
+ nicki.setNickname("Nicks");
+ friendShip2 = nicki.knows(tareq);
+ friendShip2.setYears(2);
+ friendShip3 = nicki.knows(michael);
+ friendShip3.setYears(0);
+
+ friendShip = michael.knows(david);
+ friendShip.setYears(2);
+ serialTesterGroup = new Group();
+ serialTesterGroup.setName("SDN-upgraders");
+ serialTesterGroup.addPerson(michael);
+ serialTesterGroup.addPerson(nicki);
+ serialTesterGroup.addPerson(david);
+
+ repo.save(asList(emil, david, michael, nicki, tareq));
+ friendshipRepository.save(friendShip);
+ friendshipRepository.save(friendShip2);
+ friendshipRepository.save(friendShip3);
+
+ groupRepo.save(serialTesterGroup);
+ return this;
+ }
+
+}
diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/SerializableGraphQueryRepositoryTests.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/SerializableGraphQueryRepositoryTests.java
new file mode 100644
index 000000000..2027b4f0c
--- /dev/null
+++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/SerializableGraphQueryRepositoryTests.java
@@ -0,0 +1,156 @@
+/**
+ * Copyright 2011 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.neo4j.repository;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.neo4j.model.Person;
+import org.springframework.data.neo4j.support.Neo4jTemplate;
+import org.springframework.data.neo4j.support.node.Neo4jHelper;
+import org.springframework.test.context.CleanContextCacheTestExecutionListener;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.TestExecutionListeners;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
+import org.springframework.test.context.transaction.BeforeTransaction;
+import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.io.*;
+import java.util.Date;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.neo4j.helpers.collection.IteratorUtil.asCollection;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration
+@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
+public class SerializableGraphQueryRepositoryTests {
+
+ protected final Logger log = LoggerFactory.getLogger(getClass());
+
+ @Autowired
+ private Neo4jTemplate neo4jTemplate;
+
+ @Autowired
+ private PersonRepository personRepository;
+
+ @Autowired
+ GroupRepository groupRepository;
+
+ @Autowired
+ FriendshipRepository friendshipRepository;
+
+ private SerialTesters serialTesters;
+ private Date expectedBirthDate;
+
+
+ @BeforeTransaction
+ public void cleanDb() {
+ Neo4jHelper.cleanDb(neo4jTemplate);
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ serialTesters = new SerialTesters();
+ serialTesters.createUpgraderTeam(personRepository, groupRepository, friendshipRepository);
+ expectedBirthDate = serialTesters.bdayFormatter.parse("01 JAN 2013 00:00:00");
+
+ }
+
+ @Test @Transactional
+ public void shouldBeAbleToTurnQueryResultsIntoAPOJO() throws Exception {
+ MemberDataPOJO nickisMemberData = personRepository.findMemberDataPojo(serialTesters.nicki);
+ assertPOJOContainsExpectedData(nickisMemberData);
+ }
+
+ @Test @Transactional
+ public void shouldBeAbleToSerializeAndDeserializeEntity() throws Exception {
+ Person anSDNUpgrader = personRepository.findOne(serialTesters.nicki.getId());
+ assertEntityDetailsForPerson(anSDNUpgrader);
+ Person aDeserializedSDNUpgrader = assertObjectCanBeSerializedAndDeserialized(anSDNUpgrader);
+ assertEntityDetailsForPerson(aDeserializedSDNUpgrader);
+ }
+
+ private void assertEntityDetailsForPerson(Person aPerson) {
+ assertThat(aPerson.getAge(), is(equalTo(36)));
+ assertThat(aPerson.getBoss(), is(serialTesters.tareq));
+ assertThat(aPerson.getBirthdate(), is(equalTo(expectedBirthDate)));
+ assertThat(aPerson.getName(), is(equalTo("Nicki")));
+ assertThat(aPerson.getDynamicProperty(), is(equalTo((Object)"What is this???")));
+ assertThat(aPerson.getFriendships(), hasItems(serialTesters.friendShip2, serialTesters.friendShip3)) ;
+ assertThat(aPerson.getHeight(), is(equalTo((short)100)));
+ assertThat(aPerson.getProperty("addressLine1"), is(equalTo((Object)"Somewhere")));
+ assertThat(aPerson.getProperty("addressLine2"), is(equalTo((Object)"Over the rainbow")));
+ }
+
+ @Test @Transactional
+ public void shouldBeAbleToSerializedPOJOReturnedFromQueryResult() throws Exception {
+ MemberDataPOJO nickisOrigMemberData = personRepository.findMemberDataPojo(serialTesters.nicki);
+ assertPOJOContainsExpectedData(nickisOrigMemberData);
+ MemberDataPOJO nickisDeserMemberData = assertObjectCanBeSerializedAndDeserialized(nickisOrigMemberData);
+ assertPOJOContainsExpectedData(nickisDeserMemberData);
+ }
+
+ public void assertPOJOContainsExpectedData(MemberDataPOJO pojo) throws Exception {
+ assertNotNull(pojo);
+ assertThat(pojo.getBoss(), is(serialTesters.tareq));
+ assertThat(asCollection(pojo.getTeams()), hasItem(serialTesters.serialTesterGroup));
+ assertThat(pojo.getAnInt(), is(serialTesters.tareq.getAge()));
+ assertThat(pojo.getAName(), is(serialTesters.tareq.getName()));
+ }
+
+ private T assertObjectCanBeSerializedAndDeserialized(T someObject) throws Exception {
+ assertThat(someObject, instanceOf(Serializable.class));
+ byte[] bos = serializeIt(someObject);
+ return deserializeIt(bos);
+ }
+
+ private byte[] serializeIt(T someObject) throws Exception {
+ ObjectOutputStream out = null;
+ try {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ out = new ObjectOutputStream(bos);
+ out.writeObject(someObject);
+ return bos.toByteArray();
+ } finally {
+ if (out != null) out.close();
+ }
+ }
+
+ private T deserializeIt(byte[] serializedBytes) throws Exception {
+ ObjectInputStream in = null;
+ try {
+ ByteArrayInputStream bis = new ByteArrayInputStream(serializedBytes);
+ in = new ObjectInputStream(bis);
+ Object theNewObj = in.readObject();
+ return (T)theNewObj;
+ } finally {
+ if (in != null) in.close();
+ }
+ }
+
+
+
+
+}
diff --git a/spring-data-neo4j/src/test/resources/org/springframework/data/neo4j/repository/SerializableGraphQueryRepositoryTests-context.xml b/spring-data-neo4j/src/test/resources/org/springframework/data/neo4j/repository/SerializableGraphQueryRepositoryTests-context.xml
new file mode 100644
index 000000000..08d6d11c3
--- /dev/null
+++ b/spring-data-neo4j/src/test/resources/org/springframework/data/neo4j/repository/SerializableGraphQueryRepositoryTests-context.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
\ No newline at end of file