added tests for cypher-integration, removed circular dependency check
This commit is contained in:
@@ -7,7 +7,6 @@ Changes in version 1.1.0.M1 (2011-06-13)
|
||||
* updated dependency to AspectJ 1.6.12.M1 (also available with STS 2.7.0.M2)
|
||||
* fixed errors in the REST binding (multiple property setting, index operations)
|
||||
* added sample build scripts for gradle and ant/ivy
|
||||
* cleanup of refrenced nodes/rels after transaction rollback
|
||||
* added support for the Neo4j query language "Cypher" in entity annotations, introduced methods and repositories
|
||||
* added support for self-relationships
|
||||
* elementClass annotation attribute is now optional
|
||||
|
||||
@@ -16,9 +16,6 @@
|
||||
|
||||
package org.springframework.data.graph.annotation;
|
||||
|
||||
import org.springframework.data.graph.core.FieldTraversalDescriptionBuilder;
|
||||
import org.springframework.data.graph.core.NodeBacked;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@@ -36,7 +33,7 @@ import java.lang.annotation.Target;
|
||||
* @since 15.09.2010
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
@Target({ElementType.FIELD,ElementType.METHOD})
|
||||
public @interface GraphQuery {
|
||||
/**
|
||||
* @return Query to be executed %d will be replaced by the node-id of the current entity other placeholders by the given params
|
||||
|
||||
@@ -69,10 +69,6 @@ public abstract class AbstractNodeRelationshipFieldAccessor<ENTITY extends Graph
|
||||
}
|
||||
}
|
||||
|
||||
protected void checkNoCircularReference(Node node, Set<STATE> targetNodes) {
|
||||
if (targetNodes.contains(node)) throw new InvalidDataAccessApiUsageException("Cannot create a circular reference to "+ targetNodes);
|
||||
}
|
||||
|
||||
protected Set<STATE> checkTargetIsSetOfNodebacked(Object newVal) {
|
||||
if (!(newVal instanceof Set)) {
|
||||
throw new IllegalArgumentException("New value must be a Set, was: " + newVal.getClass());
|
||||
|
||||
@@ -48,12 +48,13 @@ public class NodeDelegatingFieldAccessorFactory extends DelegatingFieldAccessorF
|
||||
return Arrays.<FieldAccessorFactory<?>>asList(
|
||||
new IdFieldAccessorFactory(),
|
||||
new TransientFieldAccessorFactory(),
|
||||
new TraversalFieldAccessorFactory(),
|
||||
new QueryFieldAccessorFactory(),
|
||||
new PropertyFieldAccessorFactory(graphDatabaseContext.getConversionService()),
|
||||
new ConvertingNodePropertyFieldAccessorFactory(graphDatabaseContext.getConversionService()),
|
||||
new SingleRelationshipFieldAccessorFactory(graphDatabaseContext),
|
||||
new OneToNRelationshipFieldAccessorFactory(graphDatabaseContext),
|
||||
new ReadOnlyOneToNRelationshipFieldAccessorFactory(graphDatabaseContext),
|
||||
new TraversalFieldAccessorFactory(),
|
||||
new OneToNRelationshipEntityFieldAccessorFactory(graphDatabaseContext)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ package org.springframework.data.graph.neo4j.fieldaccess;
|
||||
|
||||
import org.neo4j.graphdb.Direction;
|
||||
import org.neo4j.graphdb.DynamicRelationshipType;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.core.GenericCollectionTypeResolver;
|
||||
import org.springframework.data.graph.annotation.RelatedTo;
|
||||
import org.springframework.data.graph.core.NodeBacked;
|
||||
import org.springframework.data.graph.neo4j.support.GraphDatabaseContext;
|
||||
@@ -39,14 +39,15 @@ public abstract class NodeRelationshipFieldAccessorFactory implements FieldAcces
|
||||
this.graphDatabaseContext = graphDatabaseContext;
|
||||
}
|
||||
|
||||
protected Class<? extends NodeBacked> targetFrom(Field field) {
|
||||
@SuppressWarnings({"unchecked"})
|
||||
protected Class<? extends NodeBacked> targetFrom(Field field, RelatedTo relatedTo) {
|
||||
if (relatedTo!=null && relatedTo.elementClass()!=NodeBacked.class) return relatedTo.elementClass();
|
||||
if (Iterable.class.isAssignableFrom(field.getType())) {
|
||||
return (Class<? extends NodeBacked>) GenericCollectionTypeResolver.getCollectionFieldType(field);
|
||||
}
|
||||
return (Class<? extends NodeBacked>) field.getType();
|
||||
}
|
||||
|
||||
protected Class<? extends NodeBacked> targetFrom(RelatedTo relAnnotation) {
|
||||
return (Class<? extends NodeBacked>) relAnnotation.elementClass();
|
||||
}
|
||||
|
||||
protected Direction dirFrom(RelatedTo relAnnotation) {
|
||||
return relAnnotation.direction().toNeo4jDir();
|
||||
}
|
||||
@@ -69,11 +70,6 @@ public abstract class NodeRelationshipFieldAccessorFactory implements FieldAcces
|
||||
|
||||
protected boolean hasValidRelationshipAnnotation(Field field) {
|
||||
final RelatedTo relAnnotation = getRelationshipAnnotation(field);
|
||||
if (relAnnotation == null) return false;
|
||||
boolean hasElementClass = !relAnnotation.elementClass().equals(NodeBacked.class);
|
||||
if (!hasElementClass) throw new InvalidDataAccessApiUsageException(String.format(
|
||||
"Missing mandatory attribute @RelatedTo.elementClass for one-to-N relationship field %s in class: %s",
|
||||
field.getName(), field.getDeclaringClass().getName()));
|
||||
return true;
|
||||
return (relAnnotation != null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ public class OneToNRelationshipFieldAccessorFactory extends NodeRelationshipFiel
|
||||
@Override
|
||||
public FieldAccessor<NodeBacked> forField(final Field field) {
|
||||
final RelatedTo relAnnotation = getRelationshipAnnotation(field);
|
||||
return new OneToNRelationshipFieldAccessor(typeFrom(field, relAnnotation), dirFrom(relAnnotation), targetFrom(relAnnotation), graphDatabaseContext,field);
|
||||
return new OneToNRelationshipFieldAccessor(typeFrom(field, relAnnotation), dirFrom(relAnnotation), targetFrom(field, relAnnotation), graphDatabaseContext,field);
|
||||
}
|
||||
|
||||
public static class OneToNRelationshipFieldAccessor extends NodeToNodesRelationshipFieldAccessor<NodeBacked> {
|
||||
@@ -60,7 +60,6 @@ public class OneToNRelationshipFieldAccessorFactory extends NodeRelationshipFiel
|
||||
return null;
|
||||
}
|
||||
final Set<Node> targetNodes = checkTargetIsSetOfNodebacked(newVal);
|
||||
checkNoCircularReference(node, targetNodes);
|
||||
removeMissingRelationships(node, targetNodes);
|
||||
createAddedRelationships(node, targetNodes);
|
||||
return createManagedSet(entity, (Set<NodeBacked>) newVal);
|
||||
|
||||
@@ -76,16 +76,16 @@ public class QueryFieldAccessorFactory implements FieldAccessorFactory<NodeBacke
|
||||
|
||||
@Override
|
||||
public Object getValue(final NodeBacked nodeBacked) {
|
||||
final String queryString = String.format(this.query, createPlaceholderParams(nodeBacked));
|
||||
final String queryString = String.format(this.query, (Object[])createPlaceholderParams(nodeBacked));
|
||||
return doReturn(executeQuery(nodeBacked, queryString));
|
||||
}
|
||||
|
||||
private Object executeQuery(NodeBacked nodeBacked, String queryString) {
|
||||
if (iterableResult) {
|
||||
if (target.equals(Object.class)) return nodeBacked.findAllByQuery(queryString);
|
||||
nodeBacked.findAllByQuery(queryString, this.target);
|
||||
return nodeBacked.findAllByQuery(queryString, this.target);
|
||||
}
|
||||
return nodeBacked.findByQuery(query,this.target);
|
||||
return nodeBacked.findByQuery(queryString,this.target);
|
||||
}
|
||||
|
||||
private Object[] createPlaceholderParams(NodeBacked nodeBacked) {
|
||||
|
||||
@@ -39,7 +39,7 @@ public class ReadOnlyOneToNRelationshipFieldAccessorFactory extends NodeRelation
|
||||
@Override
|
||||
public FieldAccessor<NodeBacked> forField(final Field field) {
|
||||
final RelatedTo relAnnotation = getRelationshipAnnotation(field);
|
||||
return new ReadOnlyOneToNRelationshipFieldAccessor(typeFrom(field, relAnnotation), dirFrom(relAnnotation), targetFrom(relAnnotation), graphDatabaseContext,field);
|
||||
return new ReadOnlyOneToNRelationshipFieldAccessor(typeFrom(field, relAnnotation), dirFrom(relAnnotation), targetFrom(field, relAnnotation), graphDatabaseContext,field);
|
||||
}
|
||||
|
||||
public static class ReadOnlyOneToNRelationshipFieldAccessor extends OneToNRelationshipFieldAccessorFactory.OneToNRelationshipFieldAccessor {
|
||||
|
||||
@@ -44,8 +44,8 @@ public class SingleRelationshipFieldAccessorFactory extends NodeRelationshipFiel
|
||||
public FieldAccessor<NodeBacked> forField(final Field field) {
|
||||
final RelatedTo relAnnotation = getRelationshipAnnotation(field);
|
||||
if (relAnnotation == null)
|
||||
return new SingleRelationshipFieldAccessor(typeFrom(field), Direction.OUTGOING, targetFrom(field), graphDatabaseContext, field);
|
||||
return new SingleRelationshipFieldAccessor(typeFrom(field, relAnnotation), dirFrom(relAnnotation), targetFrom(field), graphDatabaseContext,field);
|
||||
return new SingleRelationshipFieldAccessor(typeFrom(field), Direction.OUTGOING, targetFrom(field, relAnnotation), graphDatabaseContext, field);
|
||||
return new SingleRelationshipFieldAccessor(typeFrom(field, relAnnotation), dirFrom(relAnnotation), targetFrom(field, relAnnotation), graphDatabaseContext,field);
|
||||
}
|
||||
|
||||
public static class SingleRelationshipFieldAccessor extends NodeToNodesRelationshipFieldAccessor<NodeBacked> {
|
||||
@@ -61,7 +61,6 @@ public class SingleRelationshipFieldAccessorFactory extends NodeRelationshipFiel
|
||||
return null;
|
||||
}
|
||||
final Set<Node> target=checkTargetIsSetOfNodebacked(Collections.singleton(newVal));
|
||||
checkNoCircularReference(node,target);
|
||||
removeMissingRelationships(node, target);
|
||||
createAddedRelationships(node,target);
|
||||
return newVal;
|
||||
|
||||
@@ -136,6 +136,8 @@ public class PartialNodeEntityState<ENTITY extends NodeBacked> extends DefaultEn
|
||||
return Arrays.<FieldAccessorFactory<?>>asList(
|
||||
//new IdFieldAccessorFactory(),
|
||||
//new TransientFieldAccessorFactory(),
|
||||
new TraversalFieldAccessorFactory(),
|
||||
new QueryFieldAccessorFactory(),
|
||||
newPropertyFieldAccessorFactory(),
|
||||
newConvertingNodePropertyFieldAccessorFactory(),
|
||||
new SingleRelationshipFieldAccessorFactory(getGraphDatabaseContext()) {
|
||||
@@ -146,7 +148,6 @@ public class PartialNodeEntityState<ENTITY extends NodeBacked> extends DefaultEn
|
||||
},
|
||||
new OneToNRelationshipFieldAccessorFactory(getGraphDatabaseContext()),
|
||||
new ReadOnlyOneToNRelationshipFieldAccessorFactory(getGraphDatabaseContext()),
|
||||
new TraversalFieldAccessorFactory(),
|
||||
new OneToNRelationshipEntityFieldAccessorFactory(getGraphDatabaseContext())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
import org.springframework.data.graph.core.TypeRepresentationStrategy;
|
||||
import org.springframework.data.graph.neo4j.support.GraphDatabaseContext;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -105,7 +106,8 @@ public class QueryExecutor {
|
||||
return new IterableWrapper<Map<String, Object>, Map<String, Object>>(result) {
|
||||
@Override
|
||||
protected Map<String, Object> underlyingObjectToObject(Map<String, Object> row) {
|
||||
for (Map.Entry<String, Object> entry : row.entrySet()) {
|
||||
Map<String,Object> newRow=new HashMap<String,Object>(row); // todo performance
|
||||
for (Map.Entry<String, Object> entry : newRow.entrySet()) {
|
||||
Object value = convertValue(entry.getValue());
|
||||
if (value != entry.getValue()) {
|
||||
entry.setValue(value);
|
||||
|
||||
@@ -43,7 +43,7 @@ public class Group {
|
||||
public final static String OTHER_NAME_INDEX="other_name";
|
||||
public static final String SEARCH_GROUPS_INDEX = "search-groups";
|
||||
|
||||
@RelatedTo(direction = Direction.OUTGOING, elementClass = Person.class)
|
||||
@RelatedTo(direction = Direction.OUTGOING)
|
||||
private Collection<Person> persons;
|
||||
|
||||
@RelatedTo(type = "persons", elementClass = Person.class)
|
||||
|
||||
@@ -16,10 +16,7 @@
|
||||
|
||||
package org.springframework.data.graph.neo4j;
|
||||
|
||||
import org.springframework.data.graph.annotation.GraphId;
|
||||
import org.springframework.data.graph.annotation.NodeEntity;
|
||||
import org.springframework.data.graph.annotation.RelatedTo;
|
||||
import org.springframework.data.graph.annotation.RelatedToVia;
|
||||
import org.springframework.data.graph.annotation.*;
|
||||
import org.springframework.data.graph.core.Direction;
|
||||
import org.springframework.data.graph.neo4j.annotation.Indexed;
|
||||
|
||||
@@ -27,9 +24,10 @@ import javax.validation.constraints.Max;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
@NodeEntity(useShortNames = false)
|
||||
@NodeEntity
|
||||
public class Person {
|
||||
|
||||
public static final String NAME_INDEX = "name_index";
|
||||
@@ -69,6 +67,34 @@ public class Person {
|
||||
@RelatedToVia(type = "knows", elementClass = Friendship.class)
|
||||
private Iterable<Friendship> friendships;
|
||||
|
||||
@GraphQuery(value = "start person=(%d) match (person)<-[:boss]-(boss) return boss")
|
||||
private Person bossByQuery;
|
||||
|
||||
@GraphQuery(value = "start person=(%d) match (person)<-[:boss]-(boss) return boss.%s",params = "name")
|
||||
private String bossName;
|
||||
|
||||
@GraphQuery(value = "start person=(%d) match (person)<-[:persons]-(team)-[:persons]->(member) return member",elementClass = Person.class)
|
||||
private Iterable<Person> otherTeamMembers;
|
||||
|
||||
@GraphQuery(value = "start person=(%d) match (person)<-[:persons]-(team)-[:persons]->(member) return member.name, member.age")
|
||||
private Iterable<Map<String,Object>> otherTeamMemberData;
|
||||
|
||||
public String getBossName() {
|
||||
return bossName;
|
||||
}
|
||||
|
||||
public Iterable<Person> getOtherTeamMembers() {
|
||||
return otherTeamMembers;
|
||||
}
|
||||
|
||||
public Iterable<Map<String, Object>> getOtherTeamMemberData() {
|
||||
return otherTeamMemberData;
|
||||
}
|
||||
|
||||
public Person getBossByQuery() {
|
||||
return bossByQuery;
|
||||
}
|
||||
|
||||
public Person() {
|
||||
}
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ public class IndexTest {
|
||||
Person me = persistedPerson(NAME_VALUE, 35);
|
||||
Person spouse = persistedPerson(NAME_VALUE3, 36);
|
||||
me.setSpouse(spouse);
|
||||
final Person foundMe = this.personRepository.findByPropertyValue(NAME_INDEX, "Person.name", NAME_VALUE);
|
||||
final Person foundMe = this.personRepository.findByPropertyValue(NAME_INDEX, "name", NAME_VALUE);
|
||||
assertEquals(spouse, foundMe.getSpouse());
|
||||
}
|
||||
|
||||
@@ -242,14 +242,14 @@ public class IndexTest {
|
||||
@Transactional
|
||||
public void testFindAllPersonByIndexOnAnnotatedField() {
|
||||
Person person = persistedPerson(NAME_VALUE, 35);
|
||||
final Person found = personRepository.findByPropertyValue(NAME_INDEX, "Person.name", NAME_VALUE);
|
||||
final Person found = personRepository.findByPropertyValue(NAME_INDEX, "name", NAME_VALUE);
|
||||
assertEquals(person, found);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findsPersonByIndexOnAnnotatedIntFieldInSeparateTransactions() {
|
||||
Person person = persistedPerson(NAME_VALUE, 35);
|
||||
final Person found = personFinder.findByPropertyValue("Person.age", 35);
|
||||
final Person found = personFinder.findByPropertyValue("age", 35);
|
||||
assertEquals("person found inside range", person, found);
|
||||
}
|
||||
|
||||
@@ -257,7 +257,7 @@ public class IndexTest {
|
||||
@Transactional
|
||||
public void testRangeQueryPersonByIndexOnAnnotatedField() {
|
||||
Person person = persistedPerson(NAME_VALUE, 35);
|
||||
final Person found = personFinder.findAllByRange("Person.age", 10, 40).iterator().next();
|
||||
final Person found = personFinder.findAllByRange("age", 10, 40).iterator().next();
|
||||
assertEquals("person found inside range", person, found);
|
||||
}
|
||||
|
||||
@@ -265,7 +265,7 @@ public class IndexTest {
|
||||
@Transactional
|
||||
public void testOutsideRangeQueryPersonByIndexOnAnnotatedField() {
|
||||
Person person = persistedPerson(NAME_VALUE, 35);
|
||||
Iterable<Person> emptyResult = personFinder.findAllByRange("Person.age", 0, 34);
|
||||
Iterable<Person> emptyResult = personFinder.findAllByRange("age", 0, 34);
|
||||
assertFalse("nothing found outside range", emptyResult.iterator().hasNext());
|
||||
}
|
||||
|
||||
@@ -275,7 +275,7 @@ public class IndexTest {
|
||||
public void testFindAllPersonByIndexOnAnnotatedFieldWithAtIndexed() {
|
||||
Person person = persistedPerson(NAME_VALUE, 35);
|
||||
person.setNickname("Mike");
|
||||
final Person found = personFinder.findByPropertyValue( "Person.nickname", "Mike");
|
||||
final Person found = personFinder.findByPropertyValue( "nickname", "Mike");
|
||||
assertEquals(person, found);
|
||||
}
|
||||
|
||||
@@ -293,11 +293,11 @@ public class IndexTest {
|
||||
@Transactional
|
||||
public void testNodeCanbBeIndexedTwice() {
|
||||
final Person p = persistedPerson(NAME_VALUE2, 30);
|
||||
Assert.assertEquals(p, personRepository.findByPropertyValue(NAME_INDEX, "Person.name", NAME_VALUE2));
|
||||
Assert.assertEquals(p, personRepository.findByPropertyValue(NAME_INDEX, "name", NAME_VALUE2));
|
||||
p.setName(NAME_VALUE);
|
||||
Assert.assertEquals(p, personRepository.findByPropertyValue(NAME_INDEX, "Person.name", NAME_VALUE));
|
||||
Assert.assertEquals(p, personRepository.findByPropertyValue(NAME_INDEX, "name", NAME_VALUE));
|
||||
p.setName(NAME_VALUE2);
|
||||
Assert.assertEquals(p, personRepository.findByPropertyValue(NAME_INDEX, "Person.name", NAME_VALUE2));
|
||||
Assert.assertEquals(p, personRepository.findByPropertyValue(NAME_INDEX, "name", NAME_VALUE2));
|
||||
}
|
||||
@Test
|
||||
public void testNodeCanbBeIndexedTwiceInDifferentTransactions() {
|
||||
@@ -310,7 +310,7 @@ public class IndexTest {
|
||||
} finally {
|
||||
tx.finish();
|
||||
}
|
||||
Assert.assertEquals(p, personRepository.findByPropertyValue(NAME_INDEX, "Person.name", NAME_VALUE2));
|
||||
Assert.assertEquals(p, personRepository.findByPropertyValue(NAME_INDEX, "name", NAME_VALUE2));
|
||||
try {
|
||||
tx = graphDatabaseContext.beginTx();
|
||||
p.setName(NAME_VALUE);
|
||||
@@ -318,7 +318,7 @@ public class IndexTest {
|
||||
} finally {
|
||||
tx.finish();
|
||||
}
|
||||
Assert.assertEquals(p, personRepository.findByPropertyValue(NAME_INDEX, "Person.name", NAME_VALUE));
|
||||
Assert.assertEquals(p, personRepository.findByPropertyValue(NAME_INDEX, "name", NAME_VALUE));
|
||||
try {
|
||||
tx = graphDatabaseContext.beginTx();
|
||||
p.setName(NAME_VALUE2);
|
||||
@@ -326,7 +326,7 @@ public class IndexTest {
|
||||
} finally {
|
||||
tx.finish();
|
||||
}
|
||||
Assert.assertEquals(p, personRepository.findByPropertyValue(NAME_INDEX, "Person.name", NAME_VALUE2));
|
||||
Assert.assertEquals(p, personRepository.findByPropertyValue(NAME_INDEX, "name", NAME_VALUE2));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -69,7 +69,7 @@ public class ModificationOutsideOfTransactionTest
|
||||
assertEquals(36, p.getAge());
|
||||
assertFalse(hasPersistentState(p));
|
||||
p.persist();
|
||||
assertEquals(36, nodeFor(p).getProperty("Person.age"));
|
||||
assertEquals(36, nodeFor(p).getProperty("age"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -160,7 +160,7 @@ public class ModificationOutsideOfTransactionTest
|
||||
Person p = persistedPerson( "Michael", 35 );
|
||||
p.setAge( 25 );
|
||||
assertEquals(25, p.getAge());
|
||||
assertEquals( 35, nodeFor( p ).getProperty("Person.age") );
|
||||
assertEquals( 35, nodeFor( p ).getProperty("age") );
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@@ -200,7 +200,7 @@ public class ModificationOutsideOfTransactionTest
|
||||
p.setSpouse( spouse );
|
||||
|
||||
assertEquals( spouse, p.getSpouse() );
|
||||
assertThat( nodeFor( p ), hasNoRelationship( "Person.spouse",spouse.getPersistentState() ) );
|
||||
assertThat( nodeFor( p ), hasNoRelationship( "spouse",spouse.getPersistentState() ) );
|
||||
|
||||
|
||||
Person spouse2 = persistedPerson( "Rana", 5 );
|
||||
@@ -218,7 +218,7 @@ public class ModificationOutsideOfTransactionTest
|
||||
p.persist();
|
||||
|
||||
assertEquals( spouse, p.getSpouse() );
|
||||
assertThat( nodeFor( p ), hasRelationship( "Person.spouse" ) );
|
||||
assertThat( nodeFor( p ), hasRelationship( "spouse" ) );
|
||||
|
||||
|
||||
Person spouse2 = persistedPerson( "Rana", 5 );
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* 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.graph.neo4j.support;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.neo4j.helpers.collection.IteratorUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.graph.neo4j.Person;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.internal.matchers.IsCollectionContaining.hasItems;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 13.06.11
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = {"classpath:org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml"})
|
||||
@Transactional
|
||||
public class NodeEntityQueryTest {
|
||||
@Autowired
|
||||
GraphDatabaseContext graphDatabaseContext;
|
||||
private TestTeam testTeam;
|
||||
private Person michael;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
testTeam = new TestTeam(graphDatabaseContext);
|
||||
testTeam.createSDGTeam();
|
||||
michael = testTeam.michael;
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void testQueryVariableRelationshipSingleResult() throws Exception {
|
||||
final Collection<Map<String,Object>> result = IteratorUtil.asCollection(michael.getOtherTeamMemberData());
|
||||
assertThat(result,hasItems(testTeam.simpleRowFor(testTeam.emil, "member"), testTeam.simpleRowFor(testTeam.david, "member")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryVariableRelationshipIterableResult() throws Exception {
|
||||
final Collection<Person> result = IteratorUtil.asCollection(michael.getOtherTeamMembers());
|
||||
|
||||
assertThat(result,hasItems(testTeam.david,testTeam.emil));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryVariableSingleResultPerson() throws Exception {
|
||||
assertEquals(testTeam.emil,michael.getBossByQuery());
|
||||
}
|
||||
@Test
|
||||
public void testQueryVariableStringResult() throws Exception {
|
||||
assertEquals(testTeam.emil.getName(),michael.getBossName());
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,7 @@ public class NodeEntityRelationshipTest {
|
||||
Person p = persistedPerson("Michael", 35);
|
||||
Person spouse = persistedPerson("Tina", 36);
|
||||
p.setSpouse(spouse);
|
||||
Node spouseNode=p.getPersistentState().getSingleRelationship(DynamicRelationshipType.withName("Person.spouse"), Direction.OUTGOING).getEndNode();
|
||||
Node spouseNode=p.getPersistentState().getSingleRelationship(DynamicRelationshipType.withName("spouse"), Direction.OUTGOING).getEndNode();
|
||||
assertEquals(spouse.getPersistentState(), spouseNode);
|
||||
assertEquals(spouse, p.getSpouse());
|
||||
}
|
||||
@@ -76,7 +76,7 @@ public class NodeEntityRelationshipTest {
|
||||
Person p = persistedPerson("Michael", 35);
|
||||
Person mother = persistedPerson("Gabi", 60);
|
||||
p.setMother(mother);
|
||||
Node motherNode = p.getPersistentState().getSingleRelationship(DynamicRelationshipType.withName("Person.mother"), Direction.OUTGOING).getEndNode();
|
||||
Node motherNode = p.getPersistentState().getSingleRelationship(DynamicRelationshipType.withName("mother"), Direction.OUTGOING).getEndNode();
|
||||
assertEquals(mother.getPersistentState(), motherNode);
|
||||
assertEquals(mother, p.getMother());
|
||||
}
|
||||
@@ -88,7 +88,7 @@ public class NodeEntityRelationshipTest {
|
||||
Person spouse = persistedPerson("Tina", 36);
|
||||
p.setSpouse(spouse);
|
||||
p.setSpouse(null);
|
||||
Assert.assertNull(p.getPersistentState().getSingleRelationship(DynamicRelationshipType.withName("Person.spouse"), Direction.OUTGOING));
|
||||
Assert.assertNull(p.getPersistentState().getSingleRelationship(DynamicRelationshipType.withName("spouse"), Direction.OUTGOING));
|
||||
Assert.assertNull(p.getSpouse());
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ public class NodeEntityRelationshipTest {
|
||||
Person friend = persistedPerson("Helga", 34);
|
||||
p.setSpouse(spouse);
|
||||
p.setSpouse(friend);
|
||||
assertEquals(friend.getPersistentState(), p.getPersistentState().getSingleRelationship(DynamicRelationshipType.withName("Person.spouse"), Direction.OUTGOING).getEndNode());
|
||||
assertEquals(friend.getPersistentState(), p.getPersistentState().getSingleRelationship(DynamicRelationshipType.withName("spouse"), Direction.OUTGOING).getEndNode());
|
||||
assertEquals(friend, p.getSpouse());
|
||||
}
|
||||
|
||||
@@ -114,11 +114,12 @@ public class NodeEntityRelationshipTest {
|
||||
assertEquals(boss, p.getBoss());
|
||||
}
|
||||
|
||||
@Test(expected = InvalidDataAccessApiUsageException.class)
|
||||
@Transactional
|
||||
public void testCircularRelationship() {
|
||||
public void testAllowsCircularRelationship() {
|
||||
Person p = persistedPerson("Michael", 35);
|
||||
p.setSpouse(p);
|
||||
p.setBoss(p);
|
||||
|
||||
assertEquals("created self-referencing relationship",p,p.getBoss());
|
||||
}
|
||||
@Test
|
||||
@Transactional
|
||||
|
||||
@@ -64,11 +64,11 @@ import static org.springframework.data.graph.neo4j.Person.persistedPerson;
|
||||
@Transactional
|
||||
public void testUserConstructor() {
|
||||
Person p = persistedPerson("Rod", 39);
|
||||
assertEquals(p.getName(), p.getPersistentState().getProperty("Person.name"));
|
||||
assertEquals(p.getAge(), p.getPersistentState().getProperty("Person.age"));
|
||||
assertEquals(p.getName(), p.getPersistentState().getProperty("name"));
|
||||
assertEquals(p.getAge(), p.getPersistentState().getProperty("age"));
|
||||
Person found = graphDatabaseContext.createEntityFromState(graphDatabaseContext.getNodeById(p.getNodeId()), Person.class);
|
||||
assertEquals("Rod", found.getPersistentState().getProperty("Person.name"));
|
||||
assertEquals(39, found.getPersistentState().getProperty("Person.age"));
|
||||
assertEquals("Rod", found.getPersistentState().getProperty("name"));
|
||||
assertEquals(39, found.getPersistentState().getProperty("age"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -82,8 +82,8 @@ import static org.springframework.data.graph.neo4j.Person.persistedPerson;
|
||||
p.setName( name );
|
||||
p.setAge( age );
|
||||
p.setHeight( height );
|
||||
assertEquals( name, p.getPersistentState().getProperty( "Person.name" ) );
|
||||
assertEquals( age, p.getPersistentState().getProperty("Person.age"));
|
||||
assertEquals( name, p.getPersistentState().getProperty( "name" ) );
|
||||
assertEquals( age, p.getPersistentState().getProperty("age"));
|
||||
assertEquals((Short)height, p.getHeight());
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ import static org.springframework.data.graph.neo4j.Person.persistedPerson;
|
||||
Person p = persistedPerson("Foo", 2);
|
||||
p.setHeight((short)182);
|
||||
assertEquals((Short)(short)182, p.getHeight());
|
||||
assertEquals((short)182, p.getPersistentState().getProperty("Person.height"));
|
||||
assertEquals((short)182, p.getPersistentState().getProperty("height"));
|
||||
}
|
||||
@Test
|
||||
@Transactional
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.graph.neo4j.support;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.neo4j.graphdb.NotFoundException;
|
||||
@@ -59,14 +60,14 @@ public class PropertyTest {
|
||||
public void testSetPropertyEnum() {
|
||||
Person p = persistedPerson("Michael", 35);
|
||||
p.setPersonality(Personality.EXTROVERT);
|
||||
assertEquals("Wrong enum serialization.", "EXTROVERT", p.getPersistentState().getProperty("Person.personality"));
|
||||
assertEquals("Wrong enum serialization.", "EXTROVERT", p.getPersistentState().getProperty("personality"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void testGetPropertyEnum() {
|
||||
Person p = persistedPerson("Michael", 35);
|
||||
p.getPersistentState().setProperty("Person.personality", "EXTROVERT");
|
||||
p.getPersistentState().setProperty("personality", "EXTROVERT");
|
||||
assertEquals("Did not deserialize property value properly.", Personality.EXTROVERT, p.getPersonality());
|
||||
}
|
||||
|
||||
@@ -75,7 +76,7 @@ public class PropertyTest {
|
||||
public void testSetTransientPropertyFieldNotManaged() {
|
||||
Person p = persistedPerson("Michael", 35);
|
||||
p.setThought("food");
|
||||
p.getPersistentState().getProperty("Person.thought");
|
||||
p.getPersistentState().getProperty("thought");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -83,7 +84,7 @@ public class PropertyTest {
|
||||
public void testGetTransientPropertyFieldNotManaged() {
|
||||
Person p = persistedPerson("Michael", 35);
|
||||
p.setThought("food");
|
||||
p.getPersistentState().setProperty("Person.thought", "sleep");
|
||||
p.getPersistentState().setProperty("thought", "sleep");
|
||||
assertEquals("Should not have read transient value from graph.", "food", p.getThought());
|
||||
}
|
||||
@Test
|
||||
@@ -143,16 +144,4 @@ public class PropertyTest {
|
||||
Friendship f = p.knows(p2);
|
||||
assertEquals("Wrong ID.", (Long)f.getPersistentState().getId(), f.getRelationshipId());
|
||||
}
|
||||
|
||||
@Test(expected = InvalidDataAccessApiUsageException.class)
|
||||
@Transactional
|
||||
public void testFailFastOnMisconfiguredOneToNProperty() {
|
||||
new InvalidOneToNEntity();
|
||||
}
|
||||
|
||||
@Test(expected = InvalidDataAccessApiUsageException.class)
|
||||
@Transactional
|
||||
public void testFailFastOnMisconfiguredReadOnlyOneToNProperty() {
|
||||
new InvalidReadOnlyOneToNEntity();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 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.graph.neo4j.support;
|
||||
|
||||
import org.neo4j.helpers.collection.MapUtil;
|
||||
import org.springframework.data.graph.neo4j.Group;
|
||||
import org.springframework.data.graph.neo4j.Person;
|
||||
import org.springframework.data.graph.neo4j.Personality;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 13.06.11
|
||||
*/
|
||||
public class TestTeam {
|
||||
private final GraphDatabaseContext graphDatabaseContext;
|
||||
public Person michael;
|
||||
public Person emil;
|
||||
public Person david;
|
||||
public Group sdg;
|
||||
|
||||
public TestTeam(GraphDatabaseContext graphDatabaseContext) {
|
||||
this.graphDatabaseContext = graphDatabaseContext;
|
||||
}
|
||||
|
||||
public void createSDGTeam() {
|
||||
michael = Person.persistedPerson("Michael", 36);
|
||||
emil = Person.persistedPerson("Emil", 30);
|
||||
michael.setBoss(emil);
|
||||
michael.setPersonality(Personality.EXTROVERT);
|
||||
david = Person.persistedPerson("David", 25);
|
||||
david.setBoss(emil);
|
||||
sdg = new Group().persist();
|
||||
sdg.setName("SDG");
|
||||
sdg.addPerson(michael);
|
||||
sdg.addPerson(emil);
|
||||
sdg.addPerson(david);
|
||||
}
|
||||
|
||||
public Map<String, Object> simpleRowFor(final Person person, String prefix) {
|
||||
return MapUtil.map(prefix+".name", person.getName(), prefix+".age", person.getAge());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* 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.graph.neo4j.support.query;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.internal.matchers.IsCollectionContaining;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.neo4j.helpers.collection.IteratorUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.graph.neo4j.Group;
|
||||
import org.springframework.data.graph.neo4j.Person;
|
||||
import org.springframework.data.graph.neo4j.Personality;
|
||||
import org.springframework.data.graph.neo4j.support.GraphDatabaseContext;
|
||||
import org.springframework.data.graph.neo4j.support.TestTeam;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 13.06.11
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = {"classpath:org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml"})
|
||||
@Transactional
|
||||
public class QueryExecutorTest {
|
||||
@Autowired
|
||||
GraphDatabaseContext graphDatabaseContext;
|
||||
private QueryExecutor queryExecutor;
|
||||
private TestTeam testTeam;
|
||||
private Person michael;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
testTeam = new TestTeam(graphDatabaseContext);
|
||||
testTeam.createSDGTeam();
|
||||
queryExecutor = new QueryExecutor(graphDatabaseContext);
|
||||
michael = testTeam.michael;
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void testQueryList() throws Exception {
|
||||
final String queryString = String.format("start person=(%d,%d) return person.name, person.age", michael.getNodeId(), testTeam.david.getNodeId());
|
||||
final Collection<Map<String,Object>> result = IteratorUtil.asCollection(queryExecutor.query(queryString));
|
||||
|
||||
assertEquals(asList(testTeam.simpleRowFor(michael,"person"),testTeam.simpleRowFor(testTeam.david,"person")),result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryListOfTypePerson() throws Exception {
|
||||
final String queryString = String.format("start person=(name_index,name,\"%s\") match (person) <-[:boss]- (boss) return boss", michael.getName());
|
||||
final Collection<Person> result = IteratorUtil.asCollection(queryExecutor.query(queryString, Person.class));
|
||||
|
||||
assertEquals(asList(testTeam.emil),result);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryOtherTeamMembers() throws Exception {
|
||||
final String queryString = String.format("start person=(%d) match (person)<-[:persons]-(team)-[:persons]->(member) return member", michael.getNodeId());
|
||||
System.out.println("testTeam = " + testTeam.sdg.getPersons());
|
||||
final Collection<Person> result = IteratorUtil.asCollection(queryExecutor.query(queryString, Person.class));
|
||||
|
||||
assertThat(result, IsCollectionContaining.hasItems(testTeam.david, testTeam.emil));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryAllTeamMembersByTeam() throws Exception {
|
||||
final String queryString = String.format("start team=(Group,name,\"%s\") match (team)-[:persons]->(member) return member", testTeam.sdg.getName());
|
||||
final Collection<Person> result = IteratorUtil.asCollection(queryExecutor.query(queryString, Person.class));
|
||||
|
||||
assertThat(result, IsCollectionContaining.hasItems(testTeam.david,testTeam.michael));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryForObjectAsGroup() throws Exception {
|
||||
final String queryString = String.format("start person=(name_index,name,\"%s\") match (person) <-[:persons]- (team) return team", michael.getName());
|
||||
final Group result = queryExecutor.queryForObject(queryString, Group.class);
|
||||
|
||||
assertEquals(testTeam.sdg,result);
|
||||
}
|
||||
@Test
|
||||
public void testQueryForObjectAsString() throws Exception {
|
||||
final String queryString = String.format("start person=(name_index,name,\"%s\") match (person) <-[:persons]- (team) return team.name", michael.getName());
|
||||
final String result = queryExecutor.queryForObject(queryString, String.class);
|
||||
|
||||
assertEquals(testTeam.sdg.getName(),result);
|
||||
}
|
||||
@Test
|
||||
public void testQueryForObjectAsEnum() throws Exception {
|
||||
final String queryString = String.format("start person=(name_index,name,\"%s\") return person.personality", michael.getName());
|
||||
final Personality result = queryExecutor.queryForObject(queryString, Personality.class);
|
||||
|
||||
assertEquals(michael.getPersonality(),result);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user