diff --git a/changelog.txt b/changelog.txt index fd0944f60..3861b0fea 100644 --- a/changelog.txt +++ b/changelog.txt @@ -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 diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/annotation/GraphQuery.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/annotation/GraphQuery.java index eab560ab5..94e12bcc5 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/annotation/GraphQuery.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/annotation/GraphQuery.java @@ -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 diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/AbstractNodeRelationshipFieldAccessor.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/AbstractNodeRelationshipFieldAccessor.java index ecaedcf4a..2d9e14c5b 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/AbstractNodeRelationshipFieldAccessor.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/AbstractNodeRelationshipFieldAccessor.java @@ -69,10 +69,6 @@ public abstract class AbstractNodeRelationshipFieldAccessor targetNodes) { - if (targetNodes.contains(node)) throw new InvalidDataAccessApiUsageException("Cannot create a circular reference to "+ targetNodes); - } - protected Set checkTargetIsSetOfNodebacked(Object newVal) { if (!(newVal instanceof Set)) { throw new IllegalArgumentException("New value must be a Set, was: " + newVal.getClass()); diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeDelegatingFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeDelegatingFieldAccessorFactory.java index 5eb9fd8f0..750a819f4 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeDelegatingFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeDelegatingFieldAccessorFactory.java @@ -48,12 +48,13 @@ public class NodeDelegatingFieldAccessorFactory extends DelegatingFieldAccessorF return Arrays.>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) ); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeRelationshipFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeRelationshipFieldAccessorFactory.java index 671c85b2d..254f9bbc2 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeRelationshipFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeRelationshipFieldAccessorFactory.java @@ -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 targetFrom(Field field) { + @SuppressWarnings({"unchecked"}) + protected Class targetFrom(Field field, RelatedTo relatedTo) { + if (relatedTo!=null && relatedTo.elementClass()!=NodeBacked.class) return relatedTo.elementClass(); + if (Iterable.class.isAssignableFrom(field.getType())) { + return (Class) GenericCollectionTypeResolver.getCollectionFieldType(field); + } return (Class) field.getType(); } - protected Class targetFrom(RelatedTo relAnnotation) { - return (Class) 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); } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/OneToNRelationshipFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/OneToNRelationshipFieldAccessorFactory.java index 1e7fcff63..a000dcbe0 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/OneToNRelationshipFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/OneToNRelationshipFieldAccessorFactory.java @@ -44,7 +44,7 @@ public class OneToNRelationshipFieldAccessorFactory extends NodeRelationshipFiel @Override public FieldAccessor 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 { @@ -60,7 +60,6 @@ public class OneToNRelationshipFieldAccessorFactory extends NodeRelationshipFiel return null; } final Set targetNodes = checkTargetIsSetOfNodebacked(newVal); - checkNoCircularReference(node, targetNodes); removeMissingRelationships(node, targetNodes); createAddedRelationships(node, targetNodes); return createManagedSet(entity, (Set) newVal); diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/QueryFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/QueryFieldAccessorFactory.java index 4d88d1aff..a16b8c014 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/QueryFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/QueryFieldAccessorFactory.java @@ -76,16 +76,16 @@ public class QueryFieldAccessorFactory implements FieldAccessorFactory 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 { diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/SingleRelationshipFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/SingleRelationshipFieldAccessorFactory.java index 5baec7566..32544a330 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/SingleRelationshipFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/SingleRelationshipFieldAccessorFactory.java @@ -44,8 +44,8 @@ public class SingleRelationshipFieldAccessorFactory extends NodeRelationshipFiel public FieldAccessor 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 { @@ -61,7 +61,6 @@ public class SingleRelationshipFieldAccessorFactory extends NodeRelationshipFiel return null; } final Set target=checkTargetIsSetOfNodebacked(Collections.singleton(newVal)); - checkNoCircularReference(node,target); removeMissingRelationships(node, target); createAddedRelationships(node,target); return newVal; diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/node/PartialNodeEntityState.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/node/PartialNodeEntityState.java index 0efd90ebb..82b982079 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/node/PartialNodeEntityState.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/node/PartialNodeEntityState.java @@ -136,6 +136,8 @@ public class PartialNodeEntityState extends DefaultEn return Arrays.>asList( //new IdFieldAccessorFactory(), //new TransientFieldAccessorFactory(), + new TraversalFieldAccessorFactory(), + new QueryFieldAccessorFactory(), newPropertyFieldAccessorFactory(), newConvertingNodePropertyFieldAccessorFactory(), new SingleRelationshipFieldAccessorFactory(getGraphDatabaseContext()) { @@ -146,7 +148,6 @@ public class PartialNodeEntityState extends DefaultEn }, new OneToNRelationshipFieldAccessorFactory(getGraphDatabaseContext()), new ReadOnlyOneToNRelationshipFieldAccessorFactory(getGraphDatabaseContext()), - new TraversalFieldAccessorFactory(), new OneToNRelationshipEntityFieldAccessorFactory(getGraphDatabaseContext()) ); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/query/QueryExecutor.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/query/QueryExecutor.java index e9270f47c..b62d77581 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/query/QueryExecutor.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/query/QueryExecutor.java @@ -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>(result) { @Override protected Map underlyingObjectToObject(Map row) { - for (Map.Entry entry : row.entrySet()) { + Map newRow=new HashMap(row); // todo performance + for (Map.Entry entry : newRow.entrySet()) { Object value = convertValue(entry.getValue()); if (value != entry.getValue()) { entry.setValue(value); diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/Group.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/Group.java index 2e497294d..f33a766d1 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/Group.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/Group.java @@ -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 persons; @RelatedTo(type = "persons", elementClass = Person.class) diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/Person.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/Person.java index 426259f67..e7d498d70 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/Person.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/Person.java @@ -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 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 otherTeamMembers; + + @GraphQuery(value = "start person=(%d) match (person)<-[:persons]-(team)-[:persons]->(member) return member.name, member.age") + private Iterable> otherTeamMemberData; + + public String getBossName() { + return bossName; + } + + public Iterable getOtherTeamMembers() { + return otherTeamMembers; + } + + public Iterable> getOtherTeamMemberData() { + return otherTeamMemberData; + } + + public Person getBossByQuery() { + return bossByQuery; + } + public Person() { } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/IndexTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/IndexTest.java index 45d58c642..cf10c1068 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/IndexTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/IndexTest.java @@ -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 emptyResult = personFinder.findAllByRange("Person.age", 0, 34); + Iterable 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 diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/ModificationOutsideOfTransactionTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/ModificationOutsideOfTransactionTest.java index 17f005dbf..dbd9072ee 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/ModificationOutsideOfTransactionTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/ModificationOutsideOfTransactionTest.java @@ -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 ); diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/NodeEntityQueryTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/NodeEntityQueryTest.java new file mode 100644 index 000000000..e18b08e7c --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/NodeEntityQueryTest.java @@ -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> 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 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()); + } +} diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/NodeEntityRelationshipTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/NodeEntityRelationshipTest.java index 9dc1f7d8c..76e2a5426 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/NodeEntityRelationshipTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/NodeEntityRelationshipTest.java @@ -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 diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/NodeEntityTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/NodeEntityTest.java index e981e2b68..736b161a7 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/NodeEntityTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/NodeEntityTest.java @@ -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 diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/PropertyTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/PropertyTest.java index 6acc26cef..be05c1cc3 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/PropertyTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/PropertyTest.java @@ -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(); - } } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/TestTeam.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/TestTeam.java new file mode 100644 index 000000000..f11cd8abc --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/TestTeam.java @@ -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 simpleRowFor(final Person person, String prefix) { + return MapUtil.map(prefix+".name", person.getName(), prefix+".age", person.getAge()); + } +} diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/query/QueryExecutorTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/query/QueryExecutorTest.java new file mode 100644 index 000000000..05020dc40 --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/query/QueryExecutorTest.java @@ -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> 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 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 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 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); + } +}