DATAGRAPH-544 Relationship Properties are not saved when using new remote implementation

This commit is contained in:
Michael Hunger
2015-03-15 10:57:47 +01:00
parent 58ba8b2c51
commit e904024afd
11 changed files with 288 additions and 55 deletions

View File

@@ -23,6 +23,8 @@ import org.neo4j.helpers.collection.IteratorUtil;
import org.neo4j.helpers.collection.IteratorWrapper;
import org.springframework.data.neo4j.aspects.Friendship;
import org.springframework.data.neo4j.aspects.Person;
import org.springframework.data.neo4j.core.GraphDatabase;
import org.springframework.data.neo4j.template.GraphCallback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
@@ -81,6 +83,25 @@ public class RelationshipEntityTests extends EntityTestBase {
friends.add(friendship);
assertEquals(1, IteratorUtil.count(friends));
assertEquals(friendship,IteratorUtil.first(friends));
assertEquals(friendship.getYears(),IteratorUtil.first(friends).getYears());
}
@Test
@Transactional
public void shouldSupportManagedSetAddOfRelationshipEntitiesSave() {
Person p = persistedPerson("Michael", 35);
Person p2 = persistedPerson("David", 25);
final Set<Friendship> friends = p.getFriendshipsSet();
assertEquals(0, IteratorUtil.count(friends));
final Friendship friendship = new Friendship(p, p2, 10);
Friendship friends2 = neo4jTemplate.save(friendship);
assertEquals(friendship, friends2);
assertEquals(friendship.getYears(),friends2.getYears());
neo4jTemplate.fetch(p);
Set<Friendship> friendships = neo4jTemplate.fetch(p.getFriendshipsSet());
Friendship friends3 = IteratorUtil.first(friendships);
assertEquals(friendship, friends3);
assertEquals(friendship.getYears(),friends3.getYears());
}
@Test
@@ -106,6 +127,21 @@ public class RelationshipEntityTests extends EntityTestBase {
assertEquals(1, getRelationshipState(f).getProperty("Friendship.years"));
}
@Test
@Transactional
public void testRelationshipSetPropertyLater() {
Person p = persistedPerson("Michael", 35);
Person p2 = persistedPerson("David", 25);
Friendship f = new Friendship(p,p2,1);
f = neo4jTemplate.save(f);
Relationship r = getRelationshipState(f);
assertEquals(1, r.getProperty("Friendship.years"));
Friendship f2 = neo4jTemplate.findOne(r.getId(), Friendship.class);
f2.setYears(2);
f2 = neo4jTemplate.save(f2);
assertEquals(2, getRelationshipState(f2).getProperty("Friendship.years"));
}
@Test
@Transactional
public void testRelationshipGetProperty() {
@@ -135,7 +171,6 @@ public class RelationshipEntityTests extends EntityTestBase {
assertEquals(f, neo4jTemplate.getRelationshipBetween(p, p2, Friendship.class, "knows"));
}
//@Ignore("The NodeBacking.getRelationshipTo() method is broken at the moment")
@Test
@Transactional
public void testGetRelationshipTo() {

View File

@@ -86,12 +86,6 @@ public interface RestAPI extends RestAPIIndex, RestAPIInternal {
// internal
RestRequest getRestRequest();
RestNode addToCache(RestNode restNode);
RestNode getFromCache(long id);
void removeFromCache(long id);
void close();
Relationship getOrCreateRelationship(Node start, Node end, RelationshipType type, Direction direction, Map<String, Object> props);

View File

@@ -104,7 +104,7 @@ public class RestAPICypherImpl implements RestAPI {
@Override
public RestNode getNodeById(long id, Load force) {
if (force != Load.ForceFromServer) {
RestNode restNode = getFromCache(id);
RestNode restNode = getNodeFromCache(id);
if (restNode != null) return restNode;
}
if (force == Load.FromCache) return new RestNode(RestNode.nodeUri(this, id), this);
@@ -116,13 +116,49 @@ public class RestAPICypherImpl implements RestAPI {
return addToCache(toNode(row));
}
public RestNode getFromCache(long id) {
return restAPI.getFromCache(id);
@Override
public RestRelationship addToCache(RestRelationship restRelationship) {
return restAPI.addToCache(restRelationship);
}
@Override
public void removeFromCache(long id) {
restAPI.removeFromCache(id);
public RestRelationship getRelationshipById(long id, Load force) {
if (force != Load.ForceFromServer) {
RestRelationship restRel = getRelFromCache(id);
if (restRel != null) return restRel;
}
if (force == Load.FromCache) return new RestRelationship(RestRelationship.relUri(this, id), this);
try {
Iterator<List<Object>> result = runQuery(GET_REL_QUERY, map("id", id)).getRows().iterator();
if (!result.hasNext()) {
throw new NotFoundException("Relationship not found " + id);
}
List<Object> row = result.next();
return addToCache(toRel(row));
} catch (NotFoundException e) {
throw e;
} catch (CypherTransactionExecutionException ctee) {
if (ctee.contains("Neo.DatabaseError.Statement.ExecutionFailure","not found")) {
throw new NotFoundException("Relationship not found " + id);
}
throw ctee;
}
}
public RestNode getNodeFromCache(long id) {
return restAPI.getNodeFromCache(id);
}
public RestRelationship getRelFromCache(long id) {
return restAPI.getRelFromCache(id);
}
@Override
public void removeNodeFromCache(long id) {
restAPI.removeNodeFromCache(id);
}
@Override
public void removeRelFromCache(long id) {
restAPI.removeRelFromCache(id);
}
@Override
@@ -130,6 +166,12 @@ public class RestAPICypherImpl implements RestAPI {
return getNodeById(id, Load.FromServer);
}
@Override
public RestRelationship getRelationshipById(long id) {
return getRelationshipById(id, Load.FromServer);
}
private RestNode toNode(List<Object> row) {
long id = ((Number) row.get(0)).longValue();
List<String> labels = (List<String>) row.get(1);
@@ -146,26 +188,6 @@ public class RestAPICypherImpl implements RestAPI {
return RestRelationship.fromCypher(id, type, props, start, end, this);
}
@Override
public RestRelationship getRelationshipById(long id) {
try {
Iterator<List<Object>> result = runQuery(GET_REL_QUERY, map("id", id)).getRows().iterator();
if (!result.hasNext()) {
throw new NotFoundException("Relationship not found " + id);
}
List<Object> row = result.next();
return toRel(row);
} catch (NotFoundException e) {
throw e;
} catch (CypherTransactionExecutionException ctee) {
if (ctee.contains("Neo.DatabaseError.Statement.ExecutionFailure","not found")) {
throw new NotFoundException("Relationship not found " + id);
}
throw ctee;
}
}
@Override
public RestNode createNode(Map<String, Object> props) {
return createNode(props, Collections.<String>emptyList());
@@ -381,9 +403,10 @@ public class RestAPICypherImpl implements RestAPI {
public void deleteEntity(RestEntity entity) {
if (entity instanceof Node) {
runQuery(_MATCH_NODE_QUERY + " DELETE n", map("id", entity.getId()));
restAPI.removeFromCache(entity.getId());
restAPI.removeNodeFromCache(entity.getId());
} else if (entity instanceof Relationship) {
runQuery(_MATCH_REL_QUERY + " DELETE r", map("id", entity.getId()));
restAPI.removeRelFromCache(entity.getId());
}
}

View File

@@ -47,8 +47,6 @@ import org.neo4j.rest.graphdb.traversal.RestTraverser;
import org.neo4j.rest.graphdb.util.JsonHelper;
import org.neo4j.rest.graphdb.util.QueryResult;
import org.neo4j.rest.graphdb.util.ResultConverter;
import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity;
import org.springframework.data.neo4j.support.mapping.Neo4jMappingContext;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
@@ -119,36 +117,63 @@ public class RestAPIImpl implements RestAPI {
return entityCache.addToCache(node);
}
@Override
public RestRelationship getRelationshipById(long id, Load force) {
if (force != Load.ForceFromServer) {
RestRelationship restRel = entityCache.getRelationship(id);
if (restRel != null) return restRel;
}
if (force == Load.FromCache) return new RestRelationship(RestRelationship.relUri(this, id),this);
// BatchRestAPI batchRestAPI = new BatchRestAPI(this);
// RestNode node = batchRestAPI.getNodeById(id);
RequestResult response = restRequest.get("relationship/" + id);
if (response.statusIs(Status.NOT_FOUND)) {
throw new NotFoundException("" + id);
}
Map<String, Object> data = (Map<String, Object>) response.toMap();
RestRelationship rel = new RestRelationship(data, this);
return entityCache.addToCache(rel);
}
@Override
public RestNode addToCache(RestNode restNode) {
return entityCache.addToCache(restNode);
}
@Override
public RestNode getFromCache(long id) {
public RestRelationship addToCache(RestRelationship rel) {
return entityCache.addToCache(rel);
}
@Override
public RestNode getNodeFromCache(long id) {
return entityCache.getNode(id);
}
@Override
public void removeFromCache(long id) {
entityCache.remove(id);
public RestRelationship getRelFromCache(long id) {
return entityCache.getRelationship(id);
}
@Override
public void removeNodeFromCache(long id) {
entityCache.removeNode(id);
}
@Override
public void removeRelFromCache(long id) {
entityCache.removeRelationship(id);
}
@Override
public RestNode getNodeById(long id) {
return getNodeById(id, Load.FromServer);
}
@Override
public RestRelationship getRelationshipById(long id) {
RequestResult requestResult = restRequest.get("relationship/" + id);
if (requestResult.statusIs(Status.NOT_FOUND)) {
throw new NotFoundException("" + id);
}
return new RestRelationship(requestResult.toMap(), this);
return getRelationshipById(id, Load.FromServer);
}
@Override
public RestNode createNode(Map<String, Object> props) {
return createNode(props,Collections.<String>emptyList());
@@ -582,7 +607,7 @@ public class RestAPIImpl implements RestAPI {
@Override
public void deleteEntity(RestEntity entity) {
getRestRequest().with(entity.getUri()).delete( "" );
entityCache.remove(entity.getId());
entityCache.removeNode(entity.getId());
}
@Override
public IndexInfo indexInfo(final String indexType) {

View File

@@ -4,6 +4,7 @@ import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.rest.graphdb.converter.RestEntityExtractor;
import org.neo4j.rest.graphdb.entity.RestEntity;
import org.neo4j.rest.graphdb.entity.RestNode;
import org.neo4j.rest.graphdb.entity.RestRelationship;
import java.util.Map;
@@ -13,6 +14,7 @@ import java.util.Map;
*/
public interface RestAPIInternal {
RestNode getNodeById(long id, RestAPI.Load force);
RestRelationship getRelationshipById(long id, RestAPI.Load force);
boolean hasToUpdate(long lastUpdate);
@@ -30,4 +32,13 @@ public interface RestAPIInternal {
FromServer,
ForceFromServer
}
RestRequest getRestRequest();
RestNode addToCache(RestNode restNode);
RestRelationship addToCache(RestRelationship restRelationship);
RestNode getNodeFromCache(long id);
RestRelationship getRelFromCache(long id);
void removeNodeFromCache(long id);
void removeRelFromCache(long id);
}

View File

@@ -9,7 +9,8 @@ import org.neo4j.rest.graphdb.RestAPI;
*/
public class RestEntityCache {
private LruCache<Long,RestNode> lruCache = new LruCache<>("RestNode",10000);
private LruCache<Long,RestNode> lruNodeCache = new LruCache<>("RestNode",10000);
private LruCache<Long,RestRelationship> lruRelCache = new LruCache<>("RestRelationship",10000);
private final RestAPI restAPI;
@@ -21,22 +22,42 @@ public class RestEntityCache {
if (node == null) return null;
long id = node.getId();
if (id != -1) {
RestNode existing = lruCache.get(id);
RestNode existing = lruNodeCache.get(id);
if (existing !=null) {
if (existing != node) existing.updateFrom(node, restAPI);
return existing;
} else {
lruCache.put(id, node);
lruNodeCache.put(id, node);
}
}
return node;
}
public RestRelationship addToCache(RestRelationship rel) {
if (rel == null) return null;
long id = rel.getId();
if (id != -1) {
RestRelationship existing = lruRelCache.get(id);
if (existing !=null) {
if (existing != rel) existing.updateFrom(rel, restAPI);
return existing;
} else {
lruRelCache.put(id, rel);
}
}
return rel;
}
public RestNode getNode(long id) {
return lruCache.get(id);
return lruNodeCache.get(id);
}
public RestRelationship getRelationship(long id) {
return lruRelCache.get(id);
}
public void remove(long id) {
lruCache.remove(id);
public void removeNode(long id) {
lruNodeCache.remove(id);
}
public void removeRelationship(long id) {
lruRelCache.remove(id);
}
}

View File

@@ -50,7 +50,7 @@ public class RestRelationship extends RestEntity implements Relationship {
@Override
protected void doUpdate() {
updateFrom(restApi.getRelationshipById(getId()), restApi);
updateFrom(restApi.getRelationshipById(getId(),RestAPIInternal.Load.ForceFromServer), restApi);
}
public Node getEndNode() {

View File

@@ -0,0 +1,123 @@
/**
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.neo4j.rest.integration;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.neo4j.graphdb.GraphDatabaseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.neo4j.annotation.*;
import org.springframework.data.neo4j.config.Neo4jConfiguration;
import org.springframework.data.neo4j.rest.SpringCypherRestGraphDatabase;
import org.springframework.data.neo4j.rest.SpringRestGraphDatabase;
import org.springframework.data.neo4j.rest.support.RestTestBase;
import org.springframework.data.neo4j.support.Neo4jTemplate;
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
import static org.junit.Assert.assertEquals;
/**
* @author mh
* @since 28.03.11
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = RestRelationshipTests.MyConfig.class)
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
public class RestRelationshipTests {
static class MyConfig extends Neo4jConfiguration {
public MyConfig() {
setBasePackage("org.springframework.data.neo4j.rest.integration");
}
@Bean
public GraphDatabaseService graphDatabaseService() {
return new SpringCypherRestGraphDatabase(RestTestBase.SERVER_ROOT_URI);
// return new SpringRestGraphDatabase(RestTestBase.SERVER_ROOT_URI);
}
}
@NodeEntity
public static class A {
@GraphId
Long id;
String name;
public A(String name) {
this.name = name;
}
public A() { }
}
@RelationshipEntity(type = "AREL")
public static class ARel {
@GraphId
Long id;
@StartNode A a1;
@EndNode
A a2;
String prop;
public ARel(A a1, A a2, String prop) {
this.a1 = a1;
this.a2 = a2;
this.prop = prop;
}
public ARel() { }
}
@Autowired
Neo4jTemplate neo4jTemplate;
@BeforeClass
public static void startDb() throws Exception {
RestTestBase.startDb();
}
@Before
public void cleanDb() {
RestTestBase.cleanDb();
}
@AfterClass
public static void shutdownDb() {
RestTestBase.shutdownDb();
}
@Test
public void testRelationshipSaveProperty() {
A a1 = neo4jTemplate.save(new A("a1"));
A a2 = neo4jTemplate.save(new A("a2"));
ARel rel = neo4jTemplate.save(new ARel(a1, a2, "foo"));
ARel rel2 = neo4jTemplate.findOne(rel.id, ARel.class);
assertEquals("foo",rel2.prop);
}
}

View File

@@ -25,7 +25,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.neo4j.aspects.support.query.QueryEngineTests;
import org.springframework.data.neo4j.core.GraphDatabase;
import org.springframework.data.neo4j.rest.SpringCypherRestGraphDatabase;
import org.springframework.data.neo4j.rest.SpringRestGraphDatabase;
import org.springframework.data.neo4j.rest.SpringCypherRestGraphDatabase;
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;

View File

@@ -36,7 +36,7 @@ import org.neo4j.server.configuration.ServerConfigurator;
import org.neo4j.test.ImpermanentGraphDatabase;
import org.springframework.data.neo4j.core.GraphDatabase;
import org.springframework.data.neo4j.rest.SpringCypherRestGraphDatabase;
import org.springframework.data.neo4j.rest.SpringRestGraphDatabase;
import org.springframework.data.neo4j.rest.SpringCypherRestGraphDatabase;
import java.util.Iterator;

View File

@@ -47,6 +47,7 @@ public class RestTestHelper
}
public GraphDatabase createGraphDatabase() throws URISyntaxException {
// return new SpringRestGraphDatabase(SERVER_ROOT_URI);
return new SpringCypherRestGraphDatabase(SERVER_ROOT_URI);
}