Merge branch 'master' of git.springsource.org:spring-data/datastore-graph

This commit is contained in:
Thomas Risberg
2010-09-10 02:05:11 -04:00
4 changed files with 106 additions and 14 deletions

View File

@@ -28,6 +28,8 @@ import org.springframework.persistence.support.AbstractTypeAnnotatingMixinFields
import org.springframework.persistence.support.EntityInstantiator;
import org.springframework.util.ObjectUtils;
import org.springframework.core.convert.ConversionService;
import javax.transaction.Status;
import javax.transaction.SystemException;
@@ -53,10 +55,14 @@ public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields<GraphEn
private FieldAccessorFactory fieldAccessorFactory;
public EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator;
private IndexService indexService;
// private ConversionService conversionService;
@Autowired
public void init(GraphDatabaseService gds, EntityInstantiator<NodeBacked, Node> graphEntityInstantiator, EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator, IndexService indexService) {
public void init(GraphDatabaseService gds, EntityInstantiator<NodeBacked, Node> graphEntityInstantiator,
EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator, IndexService indexService) {
this.graphDatabaseService = gds;
this.graphEntityInstantiator = graphEntityInstantiator;
this.relationshipEntityInstantiator = relationshipEntityInstantiator;
@@ -229,11 +235,12 @@ public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields<GraphEn
private ShouldProceedOrReturn getNodePropertyOrRelationship(Field field, NodeBacked entity) {
// TODO fix arrays, TODO serialize other types as byte[] or string (for indexing, querying) via Annotation
if (isIdField(field)) return new ShouldProceedOrReturn(entity.getUnderlyingNode().getId());
if (isPropertyType(field.getType())) {
if (Modifier.isTransient(field.getModifiers())) return new ShouldProceedOrReturn();
if (isNeo4jPropertyType(field.getType()) || isConvertableType(field.getType())) {
String propName = FieldAccessorFactory.getNeo4jPropertyName(field);
log.info("GET " + field + " <- Neo4J simple node property [" + propName + "]");
Node node = entity.getUnderlyingNode();
Object nodeProperty = node.getProperty(propName, getDefaultValue(field.getType()));
Object nodeProperty = deserializePropertyValue(node.getProperty(propName, getDefaultValue(field.getType())), field.getType());
return new ShouldProceedOrReturn(nodeProperty);
}
@@ -300,14 +307,15 @@ public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields<GraphEn
try {
if (isIdField(field)) return new ShouldProceedOrReturn(null);
if (Modifier.isFinal(field.getModifiers())) return new ShouldProceedOrReturn(newVal);
if (isPropertyType(field.getType())) {
if (Modifier.isTransient(field.getModifiers())) return new ShouldProceedOrReturn(true, newVal);
if (isNeo4jPropertyType(field.getType()) || isConvertableType(field.getType())) {
String propName = FieldAccessorFactory.getNeo4jPropertyName(field);
Node node = entity.getUnderlyingNode();
if (newVal==null) {
node.removeProperty(propName);
if (isIndexedProperty(field)) indexService.removeIndex(node,propName);
} else {
node.setProperty(propName, newVal);
node.setProperty(propName, serializePropertyValue(newVal, field.getType()));
if (isIndexedProperty(field)) indexService.index(node,propName,newVal);
}
log.info("SET " + field + " -> Neo4J simple node property [" + propName + "] with value=[" + newVal + "]");
@@ -316,7 +324,7 @@ public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields<GraphEn
FieldAccessor accessor = fieldAccessorFactory.forField(field);
if (accessor == null) {
log.info("Ignored SET " + field + ": " + field.getType().getName() + " not primitive or GraphEntity");
log.info("Ignored SET " + field + ": " + field.getType().getName() + " not primitive, convertable, or GraphEntity");
return new ShouldProceedOrReturn(true,newVal);
}
log.info("SET " + field + " -> Neo4J relationship with value=[" + newVal + "]");
@@ -377,16 +385,37 @@ public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields<GraphEn
}
}
private boolean isPropertyType(Class<?> fieldType) {
private boolean isNeo4jPropertyType(Class<?> fieldType) {
// todo: add array support
return fieldType.isPrimitive()
|| (fieldType.isArray() && !fieldType.getComponentType().isArray() && isPropertyType(fieldType.getComponentType()))
|| (fieldType.isArray() && !fieldType.getComponentType().isArray() && isNeo4jPropertyType(fieldType.getComponentType()))
|| fieldType.equals(String.class)
|| fieldType.equals(Character.class)
|| fieldType.equals(Boolean.class)
|| (fieldType.getName().startsWith("java.lang") && Number.class.isAssignableFrom(fieldType));
}
private boolean isConvertableType(Class<?> fieldType) {
//return conversionService.canConvert(fieldType, String.class);
return fieldType.isEnum();
}
private Object serializePropertyValue(Object newVal, Class<?> fieldType) {
if (isNeo4jPropertyType(fieldType)) {
return newVal;
}
// return conversionService.convert(newVal, String.class);
return ((Enum<?>)newVal).name();
}
private Object deserializePropertyValue(Object value, Class<?> fieldType) {
if (isNeo4jPropertyType(fieldType)) {
return value;
}
// return conversionService.convert(value, fieldType);
return Enum.valueOf((Class<Enum>)fieldType, (String)value);
}
// todo @property annotation
// todo fieldlist in @graphentity
private boolean isIndexedProperty(Field field) {

View File

@@ -16,6 +16,10 @@ public class Person {
private int age;
private Short height;
private transient String thought;
private Personality personality;
private Person spouse;
@@ -105,4 +109,20 @@ public class Person {
public Friendship knows(Person p) {
return (Friendship)relateTo(p, Friendship.class,"knows");
}
public void setPersonality(Personality personality) {
this.personality = personality;
}
public Personality getPersonality() {
return personality;
}
public void setThought(String thought) {
this.thought = thought;
}
public String getThought() {
return thought;
}
}

View File

@@ -0,0 +1,5 @@
package org.springframework.datastore.graph.neo4j;
public enum Personality {
EXTROVERT, INTROVERT
}

View File

@@ -18,13 +18,14 @@ import org.springframework.datastore.graph.api.NodeBacked;
import org.springframework.datastore.graph.neo4j.Friendship;
import org.springframework.datastore.graph.neo4j.Group;
import org.springframework.datastore.graph.neo4j.Person;
import org.springframework.datastore.graph.neo4j.Personality;
import org.springframework.datastore.graph.neo4j.finder.Finder;
import org.springframework.datastore.graph.neo4j.finder.FinderFactory;
import org.springframework.datastore.graph.neo4j.spi.node.Neo4jHelper;
import org.springframework.persistence.support.EntityInstantiator;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.persistence.support.EntityInstantiator;
import java.util.*;
@@ -175,7 +176,6 @@ public class Neo4jGraphPersistenceTest {
Assert.assertEquals(20,p.getAge());
}
// @Test(expected = InvalidDataAccessResourceUsageException.class)
@Test
public void testCreateRelationshipOutsideTransaction() {
Transaction tx = graphDatabaseService.beginTx();
@@ -359,6 +359,7 @@ public class Neo4jGraphPersistenceTest {
Assert.assertEquals(p, f.getPerson1());
Assert.assertEquals(p2, f.getPerson2());
}
@Test
@Transactional
public void testGetRelationshipToReturnsRelationship() {
@@ -403,6 +404,7 @@ public class Neo4jGraphPersistenceTest {
Group group = new Group();
group.setReadOnlyPersons(new HashSet<Person>());
}
@Test
@Transactional
public void testFindGroupByIndex() {
@@ -425,6 +427,7 @@ public class Neo4jGraphPersistenceTest {
final Collection<Group> result = IteratorUtil.addToCollection(found.iterator(), new HashSet<Group>());
Assert.assertEquals(new HashSet<Group>(Arrays.asList(group,group2)), result);
}
@Test
@Transactional
public void testFindAllPersonByIndexOnAnnotatedField() {
@@ -433,35 +436,70 @@ public class Neo4jGraphPersistenceTest {
final Person found = finder.findByPropertyValue("Person.name", "Michael");
Assert.assertEquals(person, found);
}
@Test
@Transactional
public void testTraverseFromGroupToPeople() {
Person p=new Person("Michael",35);
Person p = new Person("Michael", 35);
Group group = new Group();
group.setName("dev");
group.addPerson(p);
final TraversalDescription traversalDescription = new TraversalDescriptionImpl().relationships(DynamicRelationshipType.withName("persons")).filter(Traversal.returnAllButStartNode());
Iterable<Person> people=(Iterable<Person>)group.find(Person.class, traversalDescription);
Iterable<Person> people = (Iterable<Person>) group.find(Person.class, traversalDescription);
final HashSet<Person> found = new HashSet<Person>();
for (Person person : people) {
found.add(person);
}
Assert.assertEquals(Collections.singleton(p),found);
}
@Test
@Transactional
public void testTraverseFromGroupToPeopleWithFinder() {
final Finder<Person> finder = finderFactory.getFinderForClass(Person.class);
Person p=new Person("Michael",35);
Person p = new Person("Michael", 35);
Group group = new Group();
group.setName("dev");
group.addPerson(p);
final TraversalDescription traversalDescription = new TraversalDescriptionImpl().relationships(DynamicRelationshipType.withName("persons")).filter(Traversal.returnAllButStartNode());
Iterable<Person> people=finder.findAllByTraversal(group, traversalDescription);
Iterable<Person> people = finder.findAllByTraversal(group, traversalDescription);
final HashSet<Person> found = new HashSet<Person>();
for (Person person : people) {
found.add(person);
}
Assert.assertEquals(Collections.singleton(p),found);
}
@Test
@Transactional
public void testSetPropertyConversionOfEnumProperty() {
Person p = new Person("Michael", 35);
p.setPersonality(Personality.EXTROVERT);
Assert.assertEquals("Wrong enum serialization.", "EXTROVERT", p.getUnderlyingNode().getProperty("Person.personality"));
}
@Test
@Transactional
public void testGetPropertyConversionOfEnumProperty() {
Person p = new Person("Michael", 35);
p.getUnderlyingNode().setProperty("Person.personality", "EXTROVERT");
Assert.assertEquals("Did not deserialize property value properly.", Personality.EXTROVERT, p.getPersonality());
}
@Test(expected = NotFoundException.class)
@Transactional
public void testSetTransientPropertyFieldNotManaged() {
Person p = new Person("Michael", 35);
p.setThought("food");
p.getUnderlyingNode().getProperty("Person.thought");
}
@Test
@Transactional
public void testGetTransientPropertyFieldNotManaged() {
Person p = new Person("Michael", 35);
p.setThought("food");
p.getUnderlyingNode().setProperty("Person.thought", "sleep");
Assert.assertEquals("Should not have read transient value from graph.", "food", p.getThought());
}
}