DATAGRAPH-311: Initial progression towards a Serialization option for query results (and entities). Note: This relies on a change to Spring Data Commons as well ref PR https://github.com/SpringSource/spring-data-commons/pull/35

This commit is contained in:
Nicki Watt
2013-08-06 16:20:59 +01:00
parent c1c273b368
commit 8bb6313053
20 changed files with 668 additions and 76 deletions

View File

@@ -33,7 +33,7 @@
<project.type>multi</project.type>
<dist.id>spring-data-neo4j</dist.id>
<springdata.commons>1.6.0.RC1</springdata.commons>
<springdata.commons>1.6.0.BUILD-SNAPSHOT</springdata.commons>
<neo4j.version>1.9</neo4j.version>
<neo4j.spatial.version>0.11-neo4j-1.9</neo4j.spatial.version>

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 a POJO 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 POJOResult {
String value() default "";
}

View File

@@ -23,20 +23,28 @@ import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
import org.springframework.data.neo4j.support.DoReturn;
import org.springframework.data.neo4j.support.Neo4jTemplate;
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> {
public class ManagedFieldAccessorSet<T> extends AbstractSet<T> implements Serializable {
private static final long serialVersionUID = 1L;
private final Object entity;
final Set<T> 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")

View File

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

View File

@@ -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;
* <p>
* 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<String, Object> map;
protected final String prefix;

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.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<T, R> extends DefaultConverter<T, R> 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<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 extractMapResult(Object value, Class returnType, MappingPolicy mappingPolicy) {
if (!Map.class.isAssignableFrom(value.getClass())) {
@@ -95,6 +159,8 @@ public class EntityResultConverter<T, R> extends DefaultConverter<T, R> 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);
}

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,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<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<?> 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;
}
}

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,19 +81,23 @@ 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;

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

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

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

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,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> T assertObjectCanBeSerializedAndDeserialized(T someObject) throws Exception {
assertThat(someObject, instanceOf(Serializable.class));
byte[] bos = serializeIt(someObject);
return deserializeIt(bos);
}
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,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>