Merge pull request #124 from nickithewatt/DATAGRAPH-311

Datagraph 311: Serialization of Query Results and Entities
This commit is contained in:
Michael Hunger
2013-08-30 05:08:26 -07:00
24 changed files with 1161 additions and 94 deletions

View File

@@ -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 "";
}

View File

@@ -0,0 +1,34 @@
/**
* 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.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation to mark either a POJO or interface as being able to hold the results of a
* SDN based query.
*
* @author Nicki Watt
* @since 06.08.2013
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface QueryResult {
}

View File

@@ -17,27 +17,45 @@
package org.springframework.data.neo4j.fieldaccess;
import org.springframework.data.neo4j.core.EntityState;
import org.springframework.data.neo4j.mapping.MappingPolicy;
import org.springframework.data.neo4j.mapping.ManagedEntity;
import org.springframework.data.neo4j.mapping.MappingPolicy;
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
import org.springframework.data.neo4j.support.DoReturn;
import org.springframework.data.neo4j.support.Neo4jTemplate;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
/**
* This class provides a mechanism for managing and controlling access to
* a Set based field on a SDN managed entity. The associated field typically
* serves as a container for all the references to some other SDN entity(s).
*
* @param <T>
*/
public class ManagedFieldAccessorSet<T> extends AbstractSet<T> {
private final Object entity;
final Set<T> delegate;
private final Neo4jPersistentProperty property;
private final Neo4jTemplate ctx;
private final FieldAccessor fieldAccessor;
private final MappingPolicy mappingPolicy;
public class ManagedFieldAccessorSet<T> extends AbstractSet<T> implements Serializable {
private static final long serialVersionUID = 1L;
private Object writeReplace() {
return new SerializationProxy<T>(this);
}
private void readObject(ObjectInputStream ois) throws InvalidObjectException {
throw new InvalidObjectException("Proxy required");
}
private final transient Object entity;
final transient Set<T> delegate;
private final transient Neo4jPersistentProperty property;
private final transient Neo4jTemplate ctx;
private final transient FieldAccessor fieldAccessor;
private final transient MappingPolicy mappingPolicy;
@SuppressWarnings("unchecked")
public ManagedFieldAccessorSet(final Object entity, final Object newVal, final Neo4jPersistentProperty property, Neo4jTemplate ctx, FieldAccessor fieldAccessor, final MappingPolicy mappingPolicy) {
@@ -141,4 +159,26 @@ public class ManagedFieldAccessorSet<T> extends AbstractSet<T> {
delegate.clear();
update();
}
}
/**
* Implementation of the Serialization Proxy Pattern (ref Item 78
* of Effective Java - 2nd edition)
* @param <T> Type of the underlying class being stored in the Set.
*/
private static class SerializationProxy<T> implements Serializable {
private static final long serialVersionUID = 1L;
private Set<T> delegateSet;
SerializationProxy(ManagedFieldAccessorSet<T> managedFieldAccessorSet) {
this.delegateSet = managedFieldAccessorSet.delegate;
}
private Object readResolve() {
return delegateSet;
}
}
}

View File

@@ -16,25 +16,40 @@
package org.springframework.data.neo4j.fieldaccess;
import org.springframework.data.neo4j.core.EntityState;
import org.springframework.data.neo4j.mapping.MappingPolicy;
import org.springframework.data.neo4j.mapping.ManagedEntity;
import org.springframework.data.neo4j.mapping.MappingPolicy;
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
import org.springframework.data.neo4j.support.DoReturn;
import org.springframework.data.neo4j.support.Neo4jTemplate;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.Map;
/**
* Updates the entity containing such a ManagedPrefixedDynamicProperties when some property is added, changed or
* deleted.
*/
public class ManagedPrefixedDynamicProperties extends PrefixedDynamicProperties {
private final Object entity;
private final Neo4jTemplate template;
private final FieldAccessor fieldAccessor;
private final Neo4jPersistentProperty property;
private boolean isNode;
private MappingPolicy mappingPolicy;
public class ManagedPrefixedDynamicProperties extends PrefixedDynamicProperties implements Serializable
{
private static final long serialVersionUID = 1L;
private Object writeReplace() {
return new SerializationProxy(this);
}
private void readObject(ObjectInputStream ois) throws InvalidObjectException {
throw new InvalidObjectException("Proxy required");
}
private transient final Object entity;
private transient final Neo4jTemplate template;
private transient final FieldAccessor fieldAccessor;
private transient final Neo4jPersistentProperty property;
private transient boolean isNode;
private transient MappingPolicy mappingPolicy;
public ManagedPrefixedDynamicProperties(String prefix, final Neo4jPersistentProperty property, final Object entity, Neo4jTemplate template, FieldAccessor fieldAccessor, final MappingPolicy mappingPolicy) {
this(prefix,10,property,entity, template,fieldAccessor, mappingPolicy);
@@ -102,4 +117,28 @@ public class ManagedPrefixedDynamicProperties extends PrefixedDynamicProperties
property.setValue(entity, newValue);
return newValue;
}
/**
* Implementation of the Serialization Proxy Pattern (ref Item 78
* of Effective Java - 2nd edition)
* @param <T> Type of the underlying class being stored in the Set.
*/
private static class SerializationProxy<T> implements Serializable {
private static final long serialVersionUID = 1L;
private Map actualMapContent;
private String prefix;
SerializationProxy(ManagedPrefixedDynamicProperties prefixedDynamicProperties) {
this.actualMapContent = prefixedDynamicProperties.asMap();
this.prefix = prefixedDynamicProperties.prefix;
}
private Object readResolve() {
PrefixedDynamicProperties val = new PrefixedDynamicProperties(prefix);
val.setPropertiesFrom(actualMapContent);
return val;
}
}
}

View File

@@ -15,6 +15,9 @@
*/
package org.springframework.data.neo4j.fieldaccess;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@@ -27,9 +30,20 @@ import java.util.Set;
* <p>
* The methods *PrefixedProperty() allow to access the prefixed property key/values pairs directly.
*/
public class PrefixedDynamicProperties implements DynamicProperties {
private final Map<String, Object> map;
protected final String prefix;
public class PrefixedDynamicProperties implements DynamicProperties , Serializable {
private static final long serialVersionUID = 1L;
private Object writeReplace() {
return new SerializationProxy(this);
}
private void readObject(ObjectInputStream ois) throws InvalidObjectException {
throw new InvalidObjectException("Proxy required");
}
private transient final Map<String, Object> map;
protected final transient String prefix;
/**
* Handles key prefixing
@@ -291,4 +305,28 @@ public class PrefixedDynamicProperties implements DynamicProperties {
}
return true;
}
/**
* Implementation of the Serialization Proxy Pattern (ref Item 78
* of Effective Java - 2nd edition)
* @param <T> Type of the underlying class being stored in the Set.
*/
private static class SerializationProxy<T> implements Serializable {
private static final long serialVersionUID = 1L;
private Map actualMapContent;
private String prefix;
SerializationProxy(PrefixedDynamicProperties prefixedDynamicProperties) {
this.actualMapContent = prefixedDynamicProperties.map;
this.prefix = prefixedDynamicProperties.prefix;
}
private Object readResolve() {
PrefixedDynamicProperties val = new PrefixedDynamicProperties(prefix);
val.setPropertiesFrom(actualMapContent);
return val;
}
}
}

View File

@@ -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<Option> options;
public DefaultMappingPolicy(Option... options) {

View File

@@ -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.QueryResult;
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,8 +85,67 @@ public class EntityResultConverter<T, R> extends DefaultConverter<T, R> implemen
return result;
}
@SuppressWarnings("unchecked")
public R extractMapResult(Object value, Class returnType, MappingPolicy mappingPolicy) {
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("QueryResult can only be extracted from Map<String,Object>.");
}
Object newThing = null;
ResultColumnValueExtractor resultColumnValueExtractor = new ResultColumnValueExtractor((Map<String, Object>) 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 extractProxyBasedResult(Object value, Class returnType, MappingPolicy mappingPolicy) {
if (!Map.class.isAssignableFrom(value.getClass())) {
throw new RuntimeException("MapResult can only be extracted from Map<String,Object>.");
}
@@ -93,10 +157,23 @@ public class EntityResultConverter<T, R> extends DefaultConverter<T, R> implemen
@Override
public R convert(Object value, Class type, MappingPolicy mappingPolicy) {
if (type.isAnnotationPresent(MapResult.class)) {
return extractMapResult(value, type,mappingPolicy);
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);
}
}

View File

@@ -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 );
}
}

View File

@@ -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<String, Object> map;
private final MappingPolicy mappingPolicy;
private final ResultConverter converter;
private final ResultColumnValueExtractor resultColumnValueExtractor;
public QueryResultProxy(Map<String, Object> 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) {

View File

@@ -0,0 +1,111 @@
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<String, Object> map;
private final MappingPolicy mappingPolicy;
private final ResultConverter converter;
public ResultColumnValueExtractor(Map<String, Object> 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<?> classInfo = ClassTypeInformation.from(field.getDeclaringClass());
TypeInformation<?> fieldInfo = classInfo.getProperty(field.getName());
return extractFromAccessibleObject(column,fieldInfo);
}
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;
}
}

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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<Person> readOnlyPersons;
@GraphTraversal(traversal = PeopleTraversalBuilder.class, params = "persons")
private Iterable<Person> people;
transient private Iterable<Person> people;
@GraphProperty
@Indexed

View File

@@ -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,23 +81,30 @@ public class Person implements Being {
@Fetch
@RelatedToVia(type = "knows", elementClass = Friendship.class)
private Iterable<Friendship> friendships;
private Iterable<Friendship> 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<Person> otherTeamMembers;
transient private Iterable<Person> 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<Map<String,Object>> otherTeamMemberData;
transient private Iterable<Map<String,Object>> otherTeamMemberData;
// NW - all queries should be transient
@RelatedTo(elementClass = Group.class, type = "interface_test", direction = Direction.OUTGOING)
private Set<IGroup> groups;
@RelatedTo(elementClass = Person.class, type = "serialiation_test", direction = Direction.OUTGOING)
private Set<Person> serialFriends;
RootEntity root;
public RootEntity getRoot() {
@@ -305,4 +316,15 @@ public class Person implements Being {
public BestFriend getBestFriend() {
return bestFriend;
}
public Set<Person> getSerialFriends() {
if (serialFriends == null) {
serialFriends = new HashSet<Person>();
}
return serialFriends;
}
public void addSerialFriend(Person serialFriend) {
getSerialFriends().add(serialFriend);
}
}

View File

@@ -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;

View File

@@ -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<Group> getTeams();

View File

@@ -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.QueryResult;
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;
@QueryResult
public class MemberDataPOJO implements Serializable {
private static final long serialVersionUID = 1L;
@ResultColumn("collect(team)")
private Set<Group> teams;
@ResultColumn("boss")
private Person boss;
@ResultColumn("someonesAge")
private int anInt;
@ResultColumn("someonesName")
private String aName;
public Set<Group> getTeams() {
return teams;
}
public void setTeams(Set<Group> 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;
}
}

View File

@@ -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;
@@ -51,6 +51,9 @@ public interface PersonRepository extends GraphRepository<Person>, NamedIndexRep
@Query("start member=node({p_person}) match team-[:persons]->member<-[?:boss]-boss return collect(team), boss")
Iterable<MemberData> 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<MemberData> nonWorkingQuery(@Param("p_person") Person person);
@@ -92,7 +95,7 @@ public interface PersonRepository extends GraphRepository<Person>, NamedIndexRep
EndResult<Person> findByHeight( short height );
@MapResult
@QueryResult
interface NameAndPersonResult
{
@ResultColumn("name")

View File

@@ -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<Person> repo, GraphRepository<Group> groupRepo, GraphRepository<Friendship> 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;
}
}

View File

@@ -0,0 +1,265 @@
/**
* 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.fieldaccess.ManagedFieldAccessorSet;
import org.springframework.data.neo4j.fieldaccess.ManagedPrefixedDynamicProperties;
import org.springframework.data.neo4j.fieldaccess.PrefixedDynamicProperties;
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.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import java.io.*;
import java.util.Date;
import java.util.HashSet;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.neo4j.helpers.collection.IteratorUtil.asCollection;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
public class SerializableEntityRepositoryTests {
protected final Logger log = LoggerFactory.getLogger(getClass());
@Autowired
private PlatformTransactionManager transactionManager;
@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
public void shouldBeAbleToSerializeAndDeserializeBasicEntityGraph() throws Exception {
Person person = personRepository.findOne(serialTesters.nicki.getId());
assertEntityDetailsForPerson(person);
assertThat(person, instanceOf(Serializable.class));
// Do it
byte[] bos = serializeIt(person);
Person aDeserializedPerson = deserializeIt(bos);
// Verify its the same
assertEntityDetailsForPerson(aDeserializedPerson);
}
@Test
public void primitiveFieldShouldBeSerializedInOriginalForm() throws Exception {
Person deserializedPerson = assertPreSerializationSetupThenGetDeserializedPerson();
assertEquals(String.class, deserializedPerson.getName().getClass());
assertEquals("Nicki", deserializedPerson.getName());
}
@Test
public void primitiveFieldUpdatedOnDeserializedEntityShouldBeAbleToBeSavedBackToRepo() throws Exception {
final Person deserializedPerson = assertPreSerializationSetupThenGetDeserializedPerson();
assertEquals(String.class, deserializedPerson.getName().getClass());
assertEquals("Nicki", deserializedPerson.getName());
deserializedPerson.setName("New Name");
new TransactionTemplate(transactionManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
personRepository.save(deserializedPerson);
}
});
Person personFromDB = personRepository.findOne(deserializedPerson.getId());
assertEquals("New Name", personFromDB.getName());
}
@Test
public void managedFieldAkaRelationshipsShouldBeSerializedAsAHashSet() throws Exception {
Person deserializedPerson = assertPreSerializationSetupThenGetDeserializedPerson();
assertEquals(HashSet.class, deserializedPerson.getSerialFriends().getClass());
assertEquals(1, deserializedPerson.getSerialFriends().size());
assertThat(deserializedPerson.getSerialFriends(), hasItem(serialTesters.michael));
}
@Test
public void managedFieldAkaRelationshipUpdatedOnDeserializedEntityShouldBeAbleToBeSavedBackToRepo() throws Exception {
final Person deserializedPerson = assertPreSerializationSetupThenGetDeserializedPerson();
assertEquals(HashSet.class, deserializedPerson.getSerialFriends().getClass());
assertEquals(1, deserializedPerson.getSerialFriends().size());
assertThat(deserializedPerson.getSerialFriends(), hasItem(serialTesters.michael));
deserializedPerson.addSerialFriend(serialTesters.david);
assertEquals(2, deserializedPerson.getSerialFriends().size());
new TransactionTemplate(transactionManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
personRepository.save(deserializedPerson);
}
});
Person personFromDB = personRepository.findOne(deserializedPerson.getId());
assertEquals(2, personFromDB.getSerialFriends().size());
assertThat(personFromDB.getSerialFriends(), hasItem(serialTesters.michael));
assertThat(personFromDB.getSerialFriends(), hasItem(serialTesters.david));
}
@Test
public void dynamicPropertiesFieldShouldBeSerializedAsAPrefixedDynamicProperties() throws Exception {
final Person deserializedPerson = assertPreSerializationSetupThenGetDeserializedPerson();
assertEquals(PrefixedDynamicProperties.class, deserializedPerson.getPersonalProperties().getClass());
assertEquals(2, deserializedPerson.getPersonalProperties().asMap().size());
assertThat(asCollection( deserializedPerson.getPersonalProperties().getPropertyKeys()) , hasItems("addressLine1","addressLine2"));
}
@Test
public void dynamicPropertiesFieldUpdatedOnDeserializedEntityShouldBeAbleToBeSavedBackToRepo() throws Exception {
final Person deserializedPerson = assertPreSerializationSetupThenGetDeserializedPerson();
assertEquals(PrefixedDynamicProperties.class, deserializedPerson.getPersonalProperties().getClass());
assertEquals(2, deserializedPerson.getPersonalProperties().asMap().size());
assertThat(asCollection(deserializedPerson.getPersonalProperties().getPropertyKeys()) , hasItems("addressLine1", "addressLine2"));
deserializedPerson.setProperty("newDynoProp", "newDynoValue");
new TransactionTemplate(transactionManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
personRepository.save(deserializedPerson);
}
});
Person personFromDB = personRepository.findOne(deserializedPerson.getId());
assertEquals(3, personFromDB.getPersonalProperties().asMap().size());
assertThat(asCollection(personFromDB.getPersonalProperties().getPropertyKeys()) , hasItems("addressLine1", "addressLine2", "newDynoProp"));
}
private Person assertPreSerializationSetupThenGetDeserializedPerson() throws Exception {
addSerialFriend(serialTesters.nicki.getId(), serialTesters.michael);
// 1A. Make sure that before we deal with any serialization, we are still operating
// with the expected ManagedFieldAccessorSet class
final Person person = personRepository.findOne(serialTesters.nicki.getId());
assertEquals(ManagedFieldAccessorSet.class, person.getSerialFriends().getClass());
assertEquals(1, person.getSerialFriends().size());
// 1B. Make sure that before we deal with any serialization, we are still operating
// with the expected ManagedPrefixedDynamicProperties class
assertEquals(ManagedPrefixedDynamicProperties.class, person.getPersonalProperties().getClass());
assertEquals(2, person.getPersonalProperties().asMap().size());
assertThat(asCollection( person.getPersonalProperties().getPropertyKeys()) , hasItems("addressLine1","addressLine2"));
// 2. Do Serialization and return serialized object
byte[] bos = serializeIt(person);
return deserializeIt(bos);
}
private void addSerialFriend(Long sourcePersonId, final Person target) {
final Person person1 = personRepository.findOne(sourcePersonId);
new TransactionTemplate(transactionManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
person1.addSerialFriend(target);
personRepository.save(person1);
}
});
}
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> 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> 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();
}
}
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")));
}
}

View File

@@ -0,0 +1,124 @@
/**
* 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.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 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;
@BeforeTransaction
public void cleanDb() {
Neo4jHelper.cleanDb(neo4jTemplate);
}
@Before
public void setUp() throws Exception {
serialTesters = new SerialTesters();
serialTesters.createUpgraderTeam(personRepository, groupRepository, friendshipRepository);
}
@Test @Transactional
public void shouldBeAbleToTurnQueryResultIntoAPOJO() throws Exception {
MemberDataPOJO nickisMemberData = personRepository.findMemberDataPojo(serialTesters.nicki);
assertPOJOContainsExpectedData(nickisMemberData);
}
@Test @Transactional
public void shouldBeAbleToSerializedPOJOReturnedFromQueryResult() throws Exception {
MemberDataPOJO nickisOrigMemberData = personRepository.findMemberDataPojo(serialTesters.nicki);
assertPOJOContainsExpectedData(nickisOrigMemberData);
assertThat(nickisOrigMemberData, instanceOf(Serializable.class));
byte[] bos = serializeIt(nickisOrigMemberData);
MemberDataPOJO nickisDeserMemberData = deserializeIt(bos);
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> 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> 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();
}
}
}

View File

@@ -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<Group> getTeams();
@ResultColumn("boss")
Person getBoss();
}
interface APlainInterface {
Iterable<Group> getTeams();
Person getBoss();
}
@MapResult
class AConfusedPOJO {
@ResultColumn("collect(team)")
private Iterable<Group> teams;
@ResultColumn("boss")
private Person boss;
}

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:neo4j="http://www.springframework.org/schema/data/neo4j"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/data/neo4j http://www.springframework.org/schema/data/neo4j/spring-neo4j.xsd">
<context:annotation-config/>
<neo4j:config graphDatabaseService="graphDatabaseService"/>
<neo4j:repositories base-package="org.springframework.data.neo4j.repository"/>
<bean id="graphDatabaseService" class="org.neo4j.test.ImpermanentGraphDatabase" destroy-method="shutdown"/>
</beans>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:neo4j="http://www.springframework.org/schema/data/neo4j"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/data/neo4j http://www.springframework.org/schema/data/neo4j/spring-neo4j.xsd">
<context:annotation-config/>
<neo4j:config graphDatabaseService="graphDatabaseService"/>
<neo4j:repositories base-package="org.springframework.data.neo4j.repository"/>
<bean id="graphDatabaseService" class="org.neo4j.test.ImpermanentGraphDatabase" destroy-method="shutdown"/>
</beans>