diff --git a/README.md b/README.md index d6cc9fd4b..c7f3263dc 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ For more detailed questions, use the [forum](http://forum.springsource.org/forum org.springframework.data spring-data-neo4j - 1.0.0.M3 + 1.0.0.M4 @@ -103,14 +103,18 @@ For more detailed questions, use the [forum](http://forum.springsource.org/forum @Transactional public Collection makeSomeWorlds() { - World earth = new World( "Earth", 1 ); - World mars = new World( "Mars", 2 ); + World earth = world( "Earth", 1 ); + World mars = world( "Mars", 2 ); mars.addRocketRouteTo( earth ); return Arrays.asList( - new World( "Mercury", 0 ), new World( "Venus", 0 ), earth, mars, - new World( "Jupiter", 63 ), new World( "Saturn", 62 ), - new World( "Uranus", 27 ), new World( "Neptune", 13 )); + world( "Mercury", 0 ), world( "Venus", 0 ), earth, mars, + world( "Jupiter", 63 ), world( "Saturn", 62 ), + world( "Uranus", 27 ), world( "Neptune", 13 )); + } + + private World world(String name, int moons) { + return new World(name,moons).persist(); } private NodeFinder finder() { diff --git a/spring-data-graph-parent/pom.xml b/spring-data-graph-parent/pom.xml index e123d875e..b313c790e 100644 --- a/spring-data-graph-parent/pom.xml +++ b/spring-data-graph-parent/pom.xml @@ -16,7 +16,7 @@ 1.5.10 3.0.5.RELEASE 1.0.0.BUILD-SNAPSHOT - 1.3.M03 + 1.3.M04 1.6.11.M2 diff --git a/spring-data-neo4j-roo/pom.xml b/spring-data-neo4j-roo/pom.xml index d70d61616..2b5e9ca87 100644 --- a/spring-data-neo4j-roo/pom.xml +++ b/spring-data-neo4j-roo/pom.xml @@ -107,6 +107,12 @@ org.neo4j neo4j + + org.neo4j + neo4j-kernel + tests + test + diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DetachedEntityState.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DetachedEntityState.java index eefa3739e..07444746a 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DetachedEntityState.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DetachedEntityState.java @@ -36,14 +36,14 @@ import static org.springframework.data.graph.neo4j.fieldaccess.DoReturn.unwrap; */ public class DetachedEntityState, STATE> implements EntityState { private final Map dirty = new HashMap(); - private final Set backrefs = new HashSet(); protected final EntityState delegate; private final static Log log = LogFactory.getLog(DetachedEntityState.class); private GraphDatabaseContext graphDatabaseContext; - + private final BackReferences backReferences = null; public DetachedEntityState(final EntityState delegate, GraphDatabaseContext graphDatabaseContext) { this.delegate = delegate; this.graphDatabaseContext = graphDatabaseContext; + //this.backReferences = new BackReferences(this); } @Override @@ -183,10 +183,34 @@ public class DetachedEntityState, STATE> imple this.dirty.put(f, previousValue); } - public void addBackReferences(Collection backReference) { - this.backrefs.addAll(backReference); + + public GraphDatabaseContext getGraphDatabaseContext() { + return graphDatabaseContext; } + // todo always create an transaction for persist, atomic operation when no outside tx exists + @Override + public ENTITY persist() { + if (!isDetached()) return getEntity(); + Transaction tx = graphDatabaseContext.beginTx(); + try { + ENTITY result = delegate.persist(); + //persistNeighbours(); + + flushDirty(); + tx.success(); + return result; + } finally { + tx.finish(); + } + } + + private void persistNeighbours() { + backReferences.persistNeighbours(); + for (NodeBacked nodeBacked : getOutboundDirtyNodeEntities()) { + nodeBacked.persist(); + } + } private Set getOutboundDirtyNodeEntities() { HashSet result = new HashSet(); @@ -216,50 +240,9 @@ public class DetachedEntityState, STATE> imple } return false; } - - private void pruneInvalidBackRefs() { - for (Iterator it = backrefs.iterator(); it.hasNext();) { - NodeBacked backRef = it.next(); - GraphBacked entity = getEntity(); - if (backRef.refersTo(entity)) continue; - it.remove(); - } - } - public boolean refersTo(GraphBacked target) { return getOutboundDirtyNodeEntities().contains(target); } - - public GraphDatabaseContext getGraphDatabaseContext() { - return graphDatabaseContext; - } - - // todo always create an transaction for persist, atomic operation when no outside tx exists - @Override - public ENTITY persist() { - if (!isDetached()) return getEntity(); - Transaction tx = graphDatabaseContext.beginTx(); - try { - ENTITY result = delegate.persist(); - persistNeighbours(); - flushDirty(); - tx.success(); - return result; - } finally { - tx.finish(); - } - } - - private void persistNeighbours() { - pruneInvalidBackRefs(); - for (NodeBacked backref : backrefs) { - backref.persist(); - } - for (NodeBacked nodeBacked : getOutboundDirtyNodeEntities()) { - nodeBacked.persist(); - } - } - } 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 b01c68d90..dd9e9244b 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 @@ -3,6 +3,7 @@ package org.springframework.data.graph.neo4j.support; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.Assert; +import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -49,6 +50,14 @@ public class IndexTest { @Autowired private FinderFactory finderFactory; + protected NodeFinder groupFinder; + protected NodeFinder personFinder; + + @Before + public void setUp() throws Exception { + groupFinder = finderFactory.createNodeEntityFinder(Group.class); + personFinder = finderFactory.createNodeEntityFinder(Person.class); + } @BeforeTransaction public void cleanDb() { @@ -72,32 +81,41 @@ public class IndexTest { Person me = persistedPerson(NAME_VALUE, 35); Person spouse = persistedPerson(NAME_VALUE3, 36); me.setSpouse(spouse); - final NodeFinder personFinder = finderFactory.createNodeEntityFinder(Person.class); - final Person foundMe = personFinder.findByPropertyValue(Person.NAME_INDEX, "Person.name", NAME_VALUE); + final Person foundMe = this.personFinder.findByPropertyValue(Person.NAME_INDEX, "Person.name", NAME_VALUE); assertEquals(spouse, foundMe.getSpouse()); } @Test - @Transactional - @Ignore("remove property from index not workin") + //@Transactional + //@Ignore("remove property from index not workin") public void testRemovePropertyFromIndex() { - Group group = new Group().persist(); - group.setName(NAME_VALUE); - final NodeFinder finder = finderFactory.createNodeEntityFinder(Group.class); - getGroupIndex().remove(group.getPersistentState(), NAME); - final Group found = finder.findByPropertyValue(null, NAME, NAME_VALUE); + Transaction tx = graphDatabaseContext.beginTx(); + try { + Group group = new Group().persist(); + group.setName(NAME_VALUE); + getGroupIndex().remove(group.getPersistentState(), NAME); + tx.success(); + } finally { + tx.finish(); + } + final Group found = groupFinder.findByPropertyValue(null, NAME, NAME_VALUE); assertNull("Group.name removed from index", found); } @Test - @Transactional - @Ignore("remove property from index not workin") + //@Transactional + //@Ignore("remove property from index not workin") public void testRemoveNodeFromIndex() { - Group group = new Group().persist(); - group.setName(NAME_VALUE); - final NodeFinder finder = finderFactory.createNodeEntityFinder(Group.class); - getGroupIndex().remove(group.getPersistentState()); - final Group found = finder.findByPropertyValue(null, NAME, NAME_VALUE); + Transaction tx = graphDatabaseContext.beginTx(); + try { + Group group = new Group().persist(); + group.setName(NAME_VALUE); + getGroupIndex().remove(group.getPersistentState()); + tx.success(); + } finally { + tx.finish(); + } + final Group found = groupFinder.findByPropertyValue(null, NAME, NAME_VALUE); assertNull("Group.name removed from index", found); } @@ -110,8 +128,7 @@ public class IndexTest { public void testFindGroupByIndex() { Group group = new Group().persist(); group.setName(NAME_VALUE); - final NodeFinder finder = finderFactory.createNodeEntityFinder(Group.class); - final Group found = finder.findByPropertyValue(null, NAME, NAME_VALUE); + final Group found = groupFinder.findByPropertyValue(null, NAME, NAME_VALUE); assertEquals(group, found); } @@ -120,8 +137,7 @@ public class IndexTest { public void testFindGroupByAlternativeFieldNameIndex() { Group group = new Group().persist(); group.setOtherName(NAME_VALUE); - final NodeFinder finder = finderFactory.createNodeEntityFinder(Group.class); - final Group found = finder.findByPropertyValue(null, Group.OTHER_NAME_INDEX, NAME_VALUE); + final Group found = groupFinder.findByPropertyValue(null, Group.OTHER_NAME_INDEX, NAME_VALUE); assertEquals(group, found); } @@ -172,8 +188,7 @@ public class IndexTest { public void testDontFindGroupByNonIndexedFieldWithAnnotation() { Group group = new Group().persist(); group.setUnindexedName("value-unindexedName"); - final NodeFinder finder = finderFactory.createNodeEntityFinder(Group.class); - final Group found = finder.findByPropertyValue(null, "unindexedName", "value-unindexedName"); + final Group found = groupFinder.findByPropertyValue(null, "unindexedName", "value-unindexedName"); assertNull(found); } @@ -182,8 +197,7 @@ public class IndexTest { public void testDontFindGroupByNonIndexedField() { Group group = new Group().persist(); group.setUnindexedName2("value-unindexedName2"); - final NodeFinder finder = finderFactory.createNodeEntityFinder(Group.class); - final Group found = finder.findByPropertyValue(null, "unindexedName2", "value-unindexedName2"); + final Group found = groupFinder.findByPropertyValue(null, "unindexedName2", "value-unindexedName2"); assertNull(found); } @@ -194,8 +208,7 @@ public class IndexTest { group.setName(NAME_VALUE); Group group2 = new Group().persist(); group2.setName(NAME_VALUE); - final NodeFinder finder = finderFactory.createNodeEntityFinder(Group.class); - final Iterable found = finder.findAllByPropertyValue(null, NAME, NAME_VALUE); + final Iterable found = groupFinder.findAllByPropertyValue(null, NAME, NAME_VALUE); final Collection result = IteratorUtil.addToCollection(found.iterator(), new HashSet()); assertEquals(new HashSet(Arrays.asList(group, group2)), result); } @@ -205,8 +218,7 @@ public class IndexTest { public void shouldFindGroupyByQueryString() { Group group = new Group().persist(); group.setFullTextName("queryableName"); - final NodeFinder finder = finderFactory.createNodeEntityFinder(Group.class); - final Iterable found = finder.findAllByQuery(Group.SEARCH_GROUPS_INDEX, "fullTextName", "queryable*"); + final Iterable found = groupFinder.findAllByQuery(Group.SEARCH_GROUPS_INDEX, "fullTextName", "queryable*"); final Collection result = IteratorUtil.addToCollection(found.iterator(), new HashSet()); assertEquals(new HashSet(Arrays.asList(group)), result); } @@ -215,16 +227,14 @@ public class IndexTest { @Transactional public void testFindAllPersonByIndexOnAnnotatedField() { Person person = persistedPerson(NAME_VALUE, 35); - final NodeFinder finder = finderFactory.createNodeEntityFinder(Person.class); - final Person found = finder.findByPropertyValue(Person.NAME_INDEX, "Person.name", NAME_VALUE); + final Person found = personFinder.findByPropertyValue(Person.NAME_INDEX, "Person.name", NAME_VALUE); assertEquals(person, found); } @Test public void findsPersonByIndexOnAnnotatedIntFieldInSeparateTransactions() { Person person = persistedPerson(NAME_VALUE, 35); - final NodeFinder finder = finderFactory.createNodeEntityFinder(Person.class); - final Person found = finder.findByPropertyValue(null, "Person.age", 35); + final Person found = personFinder.findByPropertyValue(null, "Person.age", 35); assertEquals("person found inside range", person, found); } @@ -232,8 +242,7 @@ public class IndexTest { @Transactional public void testRangeQueryPersonByIndexOnAnnotatedField() { Person person = persistedPerson(NAME_VALUE, 35); - final NodeFinder finder = finderFactory.createNodeEntityFinder(Person.class); - final Person found = finder.findAllByRange(null, "Person.age", 10, 40).iterator().next(); + final Person found = personFinder.findAllByRange(null, "Person.age", 10, 40).iterator().next(); assertEquals("person found inside range", person, found); } @@ -241,18 +250,17 @@ public class IndexTest { @Transactional public void testOutsideRangeQueryPersonByIndexOnAnnotatedField() { Person person = persistedPerson(NAME_VALUE, 35); - final NodeFinder finder = finderFactory.createNodeEntityFinder(Person.class); - Iterable emptyResult = finder.findAllByRange(null, "Person.age", 0, 34); + Iterable emptyResult = personFinder.findAllByRange(null, "Person.age", 0, 34); assertFalse("nothing found outside range", emptyResult.iterator().hasNext()); } @Test @Transactional + public void testFindAllPersonByIndexOnAnnotatedFieldWithAtIndexed() { Person person = persistedPerson(NAME_VALUE, 35); person.setNickname("Mike"); - final NodeFinder finder = finderFactory.createNodeEntityFinder(Person.class); - final Person found = finder.findByPropertyValue(null, "Person.nickname", "Mike"); + final Person found = personFinder.findByPropertyValue(null, "Person.nickname", "Mike"); assertEquals(person, found); } 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 263a90820..68036cd1d 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 @@ -55,6 +55,7 @@ public class ModificationOutsideOfTransactionTest } @Test + @Ignore("ignored until subgraph persisting is added") public void testCreateSubgraphOutsideOfTransactionPersistInDirectionOfRel() { Person michael = new Person("Michael", 35); Person emil = new Person("Emil", 31); @@ -70,7 +71,7 @@ public class ModificationOutsideOfTransactionTest } - @Ignore + @Ignore("ignored until subgraph persisting is added") @Test public void testCreateSubgraphOutsideOfTransactionPersistInReverseDirectionOfRel() { Person michael = new Person("Michael", 35); diff --git a/spring-data-neo4j/src/test/resources/log4j.properties b/spring-data-neo4j/src/test/resources/log4j.properties index 2ad2fb6b5..8bbf9ed10 100644 --- a/spring-data-neo4j/src/test/resources/log4j.properties +++ b/spring-data-neo4j/src/test/resources/log4j.properties @@ -20,7 +20,7 @@ log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n log4j.category.org.springframework=WARN #log4j.category.org.springframework.data.graph.neo4j.support.SubReferenceNodeTypeStrategy=DEBUG #log4j.category.org.springframework.data.graph.neo4j.fieldaccess=DEBUG -log4j.category.org.springframework.data=TRACE +#log4j.category.org.springframework.data=TRACE #log4j.category.org.springframework.data.support=TRACE #log4j.category.org.springframework.persistence=TRACE #log4j.category.org.springframework.data.graph.neo4j.support=DEBUG diff --git a/src/docbkx/setup.xml b/src/docbkx/setup.xml index 05890356d..210e28822 100644 --- a/src/docbkx/setup.xml +++ b/src/docbkx/setup.xml @@ -36,7 +36,7 @@ org.springframework.data spring-data-neo4j - 1.0.0.M3 + 1.0.0.M4 diff --git a/src/main/resources/changelog.txt b/src/main/resources/changelog.txt index 33d81827e..3243a57ca 100644 --- a/src/main/resources/changelog.txt +++ b/src/main/resources/changelog.txt @@ -1,6 +1,22 @@ Spring Data Graph Changelog ============================================= +Changes in version 1.0.0.M4 (2011-03-14) +---------------------------------------- + +* update to Neo4j-1.3.M04 +* simplification of detached/attached state +* all node entities are detached at creation, must call persist() +* attach() renamed to persist() +* separate indices per domain class +* support for fulltext indices +* fixed direct lookup of numerically indexed values +* relationships via relateTo are now also restricted to one per type,direction and target +* added EntityPath/EntityPathMapper for entity based Neo4jTemplate callbacks +* added EntityEvaluator for entity based path evaluation +* error handling for node type strategy called on non type nodes +* FieldTraversalDescriptionBuilder build method parametrization + Changes in version 1.0.0.M3 (2011-02-25) ----------------------------------------