Merge branch 'master' of github.com:SpringSource/spring-data-graph
This commit is contained in:
@@ -42,7 +42,6 @@
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-neo4j</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package org.neo4j.kernel;
|
||||
|
||||
import org.neo4j.graphdb.GraphDatabaseService;
|
||||
import org.neo4j.kernel.Config;
|
||||
import org.neo4j.kernel.IdGeneratorFactory;
|
||||
import org.neo4j.kernel.impl.core.*;
|
||||
import org.neo4j.kernel.impl.nioneo.store.FileSystemAbstraction;
|
||||
import org.neo4j.kernel.impl.nioneo.store.StoreId;
|
||||
@@ -48,7 +46,7 @@ public class RestConfig extends Config {
|
||||
}
|
||||
|
||||
private static class NullTransactionManager implements TransactionManager {
|
||||
private static final Transaction TRANSACTION = new Transaction() {
|
||||
private static final Transaction NULL_JAVA_TRANSACTION = new Transaction() {
|
||||
@Override
|
||||
public void commit() throws HeuristicMixedException, HeuristicRollbackException, RollbackException, SecurityException, SystemException {
|
||||
|
||||
@@ -102,7 +100,7 @@ public class RestConfig extends Config {
|
||||
|
||||
@Override
|
||||
public Transaction getTransaction() throws SystemException {
|
||||
return TRANSACTION;
|
||||
return NULL_JAVA_TRANSACTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -127,7 +125,7 @@ public class RestConfig extends Config {
|
||||
|
||||
@Override
|
||||
public Transaction suspend() throws SystemException {
|
||||
return TRANSACTION;
|
||||
return NULL_JAVA_TRANSACTION;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package org.neo4j.rest.graphdb;
|
||||
|
||||
import org.neo4j.graphdb.GraphDatabaseService;
|
||||
import org.neo4j.kernel.EmbeddedGraphDatabase;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 25.01.11
|
||||
*/
|
||||
public class GraphDatabaseFactory {
|
||||
public static GraphDatabaseService databaseFor(String url) {
|
||||
return databaseFor( url, null,null );
|
||||
}
|
||||
|
||||
public static GraphDatabaseService databaseFor(String url, String username, String password) {
|
||||
if (url.startsWith( "http://" ) || url.startsWith( "https://" )) {
|
||||
return new RestGraphDatabase( toURI( url ), username,password );
|
||||
}
|
||||
String path=url;
|
||||
if (url.startsWith( "file:" )) {
|
||||
path = toURI( url ).getPath();
|
||||
}
|
||||
File file = new File( path );
|
||||
if (!file.isDirectory()) file=file.getParentFile();
|
||||
return new EmbeddedGraphDatabase( file.getAbsolutePath() );
|
||||
}
|
||||
|
||||
private static URI toURI( String uri ) {
|
||||
try {
|
||||
return new URI(uri);
|
||||
} catch ( URISyntaxException e ) {
|
||||
throw new RuntimeException( "Error using URI "+uri, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.neo4j.rest.graphdb;
|
||||
|
||||
import com.sun.jersey.api.client.ClientResponse;
|
||||
import org.neo4j.graphdb.GraphDatabaseService;
|
||||
import org.neo4j.graphdb.NotFoundException;
|
||||
import org.neo4j.graphdb.PropertyContainer;
|
||||
import org.neo4j.helpers.collection.IterableWrapper;
|
||||
@@ -154,7 +155,11 @@ public class RestEntity implements PropertyContainer {
|
||||
return getClass().equals( o.getClass() ) && getId() == ( (RestEntity) o ).getId();
|
||||
}
|
||||
|
||||
public RestGraphDatabase getGraphDatabase() {
|
||||
public GraphDatabaseService getGraphDatabase() {
|
||||
throw new UnsupportedOperationException("No GraphDatabaseService semantics for the REST-API");
|
||||
}
|
||||
|
||||
public RestGraphDatabase getRestGraphDatabase() {
|
||||
return graphDatabase;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,18 +4,22 @@ import com.sun.jersey.api.client.ClientResponse;
|
||||
import org.neo4j.graphdb.*;
|
||||
import org.neo4j.graphdb.event.KernelEventHandler;
|
||||
import org.neo4j.graphdb.event.TransactionEventHandler;
|
||||
import org.neo4j.graphdb.index.IndexManager;
|
||||
import org.neo4j.graphdb.index.Index;
|
||||
import org.neo4j.graphdb.traversal.TraversalDescription;
|
||||
import org.neo4j.kernel.AbstractGraphDatabase;
|
||||
import org.neo4j.kernel.Config;
|
||||
import org.neo4j.kernel.RestConfig;
|
||||
import org.neo4j.rest.graphdb.index.RestIndexManager;
|
||||
import org.springframework.data.graph.core.GraphDatabase;
|
||||
import org.springframework.data.graph.core.Property;
|
||||
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
import java.io.Serializable;
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
public class RestGraphDatabase extends AbstractGraphDatabase {
|
||||
public class RestGraphDatabase extends AbstractGraphDatabase implements GraphDatabase {
|
||||
|
||||
private RestRequest restRequest;
|
||||
private long propertyRefetchTimeInMillis = 1000;
|
||||
|
||||
@@ -28,86 +32,62 @@ public class RestGraphDatabase extends AbstractGraphDatabase {
|
||||
restRequest = new RestRequest( uri, user, password );
|
||||
}
|
||||
|
||||
public Transaction beginTx() {
|
||||
return new Transaction() {
|
||||
public void success() {
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
|
||||
}
|
||||
|
||||
public void failure() {
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public Node getNodeById(long id) {
|
||||
ClientResponse response = restRequest.get("node/" + id);
|
||||
if ( restRequest.statusIs(response, Status.NOT_FOUND) ) {
|
||||
throw new NotFoundException( "" + id );
|
||||
}
|
||||
return new RestNode( restRequest.toMap(response), this );
|
||||
}
|
||||
|
||||
public <T> TransactionEventHandler<T> registerTransactionEventHandler( TransactionEventHandler<T> tTransactionEventHandler ) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public <T> TransactionEventHandler<T> unregisterTransactionEventHandler( TransactionEventHandler<T> tTransactionEventHandler ) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public KernelEventHandler registerKernelEventHandler( KernelEventHandler kernelEventHandler ) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public KernelEventHandler unregisterKernelEventHandler( KernelEventHandler kernelEventHandler ) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public IndexManager index() {
|
||||
return new RestIndexManager( restRequest, this );
|
||||
}
|
||||
|
||||
public Node createNode() {
|
||||
ClientResponse response = restRequest.post( "node", null );
|
||||
if ( restRequest.statusOtherThan( response, Status.CREATED ) ) {
|
||||
@Override
|
||||
public Node createNode(Property... props) {
|
||||
ClientResponse response = restRequest.post("node", null);
|
||||
if ( restRequest.statusOtherThan(response, Status.CREATED) ) {
|
||||
throw new RuntimeException( "" + response.getStatus() );
|
||||
}
|
||||
return new RestNode( response.getLocation(), this );
|
||||
}
|
||||
|
||||
public boolean enableRemoteShell() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public boolean enableRemoteShell( Map<String, Serializable> config ) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public Iterable<Node> getAllNodes() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public Node getNodeById( long id ) {
|
||||
ClientResponse response = restRequest.get( "node/" + id );
|
||||
if ( restRequest.statusIs( response, Status.NOT_FOUND ) ) {
|
||||
throw new NotFoundException( "" + id );
|
||||
}
|
||||
return new RestNode( restRequest.toMap( response ), this );
|
||||
}
|
||||
|
||||
public Node getReferenceNode() {
|
||||
Map<?, ?> map = restRequest.toMap( restRequest.get( "" ) );
|
||||
return new RestNode( (String) map.get( "reference_node" ), this );
|
||||
}
|
||||
|
||||
public Relationship getRelationshipById( long id ) {
|
||||
ClientResponse response = restRequest.get( "relationship/" + id );
|
||||
@Override
|
||||
public Relationship getRelationshipById(long id) {
|
||||
ClientResponse response = restRequest.get("relationship/" + id);
|
||||
if ( restRequest.statusIs( response, Status.NOT_FOUND ) ) {
|
||||
throw new NotFoundException( "" + id );
|
||||
}
|
||||
return new RestRelationship( restRequest.toMap( response ), this );
|
||||
}
|
||||
|
||||
public Iterable<RelationshipType> getRelationshipTypes() {
|
||||
throw new UnsupportedOperationException();
|
||||
@Override
|
||||
public Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Property... props) {
|
||||
Relationship relationship = startNode.createRelationshipTo(endNode, type);
|
||||
return relationship;
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
@Override
|
||||
public <T extends PropertyContainer> Index<T> getIndex(String indexName) {
|
||||
return (Index<T>) index().forNodes(indexName); // todo
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends PropertyContainer> Index<T> createIndex(Class<T> type, String indexName, boolean fullText) {
|
||||
return (Index<T>) index().forNodes(indexName); // todo
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraversalDescription createTraversalDescription() {
|
||||
return new RestTraversal();
|
||||
}
|
||||
|
||||
public RestIndexManager index() {
|
||||
return new RestIndexManager( restRequest, this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getReferenceNode() {
|
||||
Map<?, ?> map = restRequest.toMap( restRequest.get( "" ) );
|
||||
return new RestNode( (String) map.get( "reference_node" ), this );
|
||||
}
|
||||
|
||||
public RestRequest getRestRequest() {
|
||||
@@ -117,9 +97,77 @@ public class RestGraphDatabase extends AbstractGraphDatabase {
|
||||
public long getPropertyRefetchTimeInMillis() {
|
||||
return propertyRefetchTimeInMillis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node createNode() {
|
||||
return createNode((Property[])null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Node> getAllNodes() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<RelationshipType> getRelationshipTypes() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enableRemoteShell() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enableRemoteShell(Map<String, Serializable> initialProperties) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transaction beginTx() {
|
||||
return new Transaction() {
|
||||
@Override
|
||||
public void failure() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void success() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> TransactionEventHandler<T> registerTransactionEventHandler(TransactionEventHandler<T> handler) {
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> TransactionEventHandler<T> unregisterTransactionEventHandler(TransactionEventHandler<T> handler) {
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KernelEventHandler registerKernelEventHandler(KernelEventHandler handler) {
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KernelEventHandler unregisterKernelEventHandler(KernelEventHandler handler) {
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStoreDir() {
|
||||
return restRequest.getUri().toString();
|
||||
return getRestRequest().getUri().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -35,7 +35,7 @@ public class RestNode extends RestEntity implements Node {
|
||||
if ( restRequest.statusOtherThan( response, Status.CREATED ) ) {
|
||||
throw new RuntimeException( "" + response.getStatus() );
|
||||
}
|
||||
return new RestRelationship( response.getLocation(), getGraphDatabase() );
|
||||
return new RestRelationship( response.getLocation(), getRestGraphDatabase() );
|
||||
}
|
||||
|
||||
public Iterable<Relationship> getRelationships() {
|
||||
@@ -48,7 +48,7 @@ public class RestNode extends RestEntity implements Node {
|
||||
(Collection<Object>) restRequest.toEntity( response ) ) {
|
||||
@Override
|
||||
protected Relationship underlyingObjectToObject( Object data ) {
|
||||
return new RestRelationship( (Map<?, ?>) data, getGraphDatabase() );
|
||||
return new RestRelationship( (Map<?, ?>) data, getRestGraphDatabase() );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ public class RestRelationship extends RestEntity implements Relationship {
|
||||
}
|
||||
|
||||
private RestNode node( String uri ) {
|
||||
return new RestNode( uri, getGraphDatabase() );
|
||||
return new RestNode( uri, getRestGraphDatabase() );
|
||||
}
|
||||
|
||||
public Node getStartNode() {
|
||||
|
||||
@@ -207,7 +207,7 @@ public class RestTraversal implements RestTraversalDescription
|
||||
{
|
||||
throw new RuntimeException( String.format( "Unexpected traversal result, %s instead of collection", col != null ? col.getClass() : null ) );
|
||||
}
|
||||
return new RestTraverser( (Collection)col, restNode.getGraphDatabase() );
|
||||
return new RestTraverser( (Collection)col, restNode.getRestGraphDatabase() );
|
||||
}
|
||||
|
||||
public static RestTraversalDescription description()
|
||||
|
||||
@@ -22,13 +22,13 @@ public class RestRelationshipIndex extends RestIndex<Relationship> implements Re
|
||||
return Relationship.class;
|
||||
}
|
||||
|
||||
public void remove(Relationship entity, String key) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void remove(Relationship entity) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
// public void remove(Relationship entity, String key) {
|
||||
// throw new UnsupportedOperationException();
|
||||
// }
|
||||
//
|
||||
// public void remove(Relationship entity) {
|
||||
// throw new UnsupportedOperationException();
|
||||
// }
|
||||
|
||||
protected Relationship createEntity( Map<?, ?> item ) {
|
||||
return new RestRelationship( (Map<?, ?>) item, restGraphDatabase );
|
||||
|
||||
@@ -1,23 +1,13 @@
|
||||
package org.neo4j.rest.graphdb;
|
||||
|
||||
|
||||
import com.sun.jersey.api.client.Client;
|
||||
import com.sun.jersey.api.client.ClientResponse;
|
||||
import org.apache.log4j.BasicConfigurator;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.neo4j.graphdb.Direction;
|
||||
import org.neo4j.graphdb.GraphDatabaseService;
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.Relationship;
|
||||
import org.neo4j.server.AddressResolver;
|
||||
import org.neo4j.server.NeoServerWithEmbeddedWebServer;
|
||||
import org.neo4j.server.modules.RESTApiModule;
|
||||
import org.neo4j.server.modules.ThirdPartyJAXRSModule;
|
||||
import org.neo4j.server.startup.healthcheck.StartupHealthCheck;
|
||||
import org.neo4j.server.web.Jetty6WebServer;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
@@ -25,7 +15,7 @@ import java.util.Iterator;
|
||||
|
||||
public class RestTestBase {
|
||||
|
||||
protected GraphDatabaseService graphDb;
|
||||
protected RestGraphDatabase graphDb;
|
||||
private static final String HOSTNAME = "localhost";
|
||||
private static final int PORT = 7473;
|
||||
private static LocalTestServer neoServer = new LocalTestServer(HOSTNAME,PORT).withPropertiesFile("test-db.properties");
|
||||
@@ -47,11 +37,6 @@ public class RestTestBase {
|
||||
neoServer.cleanDb();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
graphDb.shutdown();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void shutdownDb() {
|
||||
neoServer.stop();
|
||||
@@ -61,7 +46,7 @@ public class RestTestBase {
|
||||
protected Relationship relationship() {
|
||||
Iterator<Relationship> it = node().getRelationships(Direction.OUTGOING).iterator();
|
||||
if (it.hasNext()) return it.next();
|
||||
return node().createRelationshipTo(graphDb.createNode(), Type.TEST);
|
||||
return node().createRelationshipTo(graphDb.createNode(null), Type.TEST);
|
||||
}
|
||||
|
||||
protected Node node() {
|
||||
|
||||
@@ -24,20 +24,20 @@ import org.springframework.test.context.transaction.TransactionalTestExecutionLi
|
||||
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
|
||||
public class RestEntityPropertyValidationTest extends EntityPropertyValidationTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void startDb() throws Exception {
|
||||
RestTestBase.startDb();
|
||||
}
|
||||
@BeforeClass
|
||||
public static void startDb() throws Exception {
|
||||
RestTestBase.startDb();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void cleanDb() {
|
||||
RestTestBase.cleanDb();
|
||||
}
|
||||
@Before
|
||||
public void cleanDb() {
|
||||
RestTestBase.cleanDb();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void shutdownDb() {
|
||||
RestTestBase.shutdownDb();
|
||||
|
||||
}
|
||||
@AfterClass
|
||||
public static void shutdownDb() {
|
||||
RestTestBase.shutdownDb();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,8 +5,7 @@ import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.neo4j.rest.graphdb.RestTestBase;
|
||||
import org.springframework.data.graph.neo4j.support.FinderTest;
|
||||
import org.springframework.data.graph.neo4j.support.IndexTest;
|
||||
import org.springframework.data.graph.neo4j.support.GraphRepositoryTest;
|
||||
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
@@ -22,22 +21,22 @@ import org.springframework.test.context.transaction.TransactionalTestExecutionLi
|
||||
@ContextConfiguration(locations = {"classpath:org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml",
|
||||
"classpath:RestTest-context.xml"})
|
||||
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
|
||||
public class RestFinderTest extends FinderTest {
|
||||
public class RestFinderTest extends GraphRepositoryTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void startDb() throws Exception {
|
||||
RestTestBase.startDb();
|
||||
}
|
||||
@BeforeClass
|
||||
public static void startDb() throws Exception {
|
||||
RestTestBase.startDb();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void cleanDb() {
|
||||
RestTestBase.cleanDb();
|
||||
}
|
||||
@Before
|
||||
public void cleanDb() {
|
||||
RestTestBase.cleanDb();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void shutdownDb() {
|
||||
RestTestBase.shutdownDb();
|
||||
|
||||
}
|
||||
@AfterClass
|
||||
public static void shutdownDb() {
|
||||
RestTestBase.shutdownDb();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,20 +27,20 @@ import java.net.NoRouteToHostException;
|
||||
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
|
||||
public class RestNodeEntityRelationshipTest extends NodeEntityRelationshipTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void startDb() throws Exception {
|
||||
RestTestBase.startDb();
|
||||
}
|
||||
@BeforeClass
|
||||
public static void startDb() throws Exception {
|
||||
RestTestBase.startDb();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void cleanDb() {
|
||||
RestTestBase.cleanDb();
|
||||
}
|
||||
@Before
|
||||
public void cleanDb() {
|
||||
RestTestBase.cleanDb();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void shutdownDb() {
|
||||
RestTestBase.shutdownDb();
|
||||
|
||||
}
|
||||
@AfterClass
|
||||
public static void shutdownDb() {
|
||||
RestTestBase.shutdownDb();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import org.junit.BeforeClass;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.neo4j.rest.graphdb.RestTestBase;
|
||||
import org.springframework.data.graph.neo4j.support.ProjectionTest;
|
||||
import org.springframework.data.graph.neo4j.support.RelationshipEntityTest;
|
||||
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
@@ -24,20 +23,20 @@ import org.springframework.test.context.transaction.TransactionalTestExecutionLi
|
||||
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
|
||||
public class RestProjectionTest extends ProjectionTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void startDb() throws Exception {
|
||||
RestTestBase.startDb();
|
||||
}
|
||||
@BeforeClass
|
||||
public static void startDb() throws Exception {
|
||||
RestTestBase.startDb();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void cleanDb() {
|
||||
RestTestBase.cleanDb();
|
||||
}
|
||||
@Before
|
||||
public void cleanDb() {
|
||||
RestTestBase.cleanDb();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void shutdownDb() {
|
||||
RestTestBase.shutdownDb();
|
||||
|
||||
}
|
||||
@AfterClass
|
||||
public static void shutdownDb() {
|
||||
RestTestBase.shutdownDb();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,20 +24,20 @@ import org.springframework.test.context.transaction.TransactionalTestExecutionLi
|
||||
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
|
||||
public class RestPropertyTest extends PropertyTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void startDb() throws Exception {
|
||||
RestTestBase.startDb();
|
||||
}
|
||||
@BeforeClass
|
||||
public static void startDb() throws Exception {
|
||||
RestTestBase.startDb();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void cleanDb() {
|
||||
RestTestBase.cleanDb();
|
||||
}
|
||||
@Before
|
||||
public void cleanDb() {
|
||||
RestTestBase.cleanDb();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void shutdownDb() {
|
||||
RestTestBase.shutdownDb();
|
||||
|
||||
}
|
||||
@AfterClass
|
||||
public static void shutdownDb() {
|
||||
RestTestBase.shutdownDb();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,20 +24,20 @@ import org.springframework.test.context.transaction.TransactionalTestExecutionLi
|
||||
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
|
||||
public class RestRelationshipEntityTest extends RelationshipEntityTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void startDb() throws Exception {
|
||||
RestTestBase.startDb();
|
||||
}
|
||||
@BeforeClass
|
||||
public static void startDb() throws Exception {
|
||||
RestTestBase.startDb();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void cleanDb() {
|
||||
RestTestBase.cleanDb();
|
||||
}
|
||||
@Before
|
||||
public void cleanDb() {
|
||||
RestTestBase.cleanDb();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void shutdownDb() {
|
||||
RestTestBase.shutdownDb();
|
||||
|
||||
}
|
||||
@AfterClass
|
||||
public static void shutdownDb() {
|
||||
RestTestBase.shutdownDb();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package org.springframework.data.graph.core;
|
||||
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.PropertyContainer;
|
||||
import org.neo4j.graphdb.Relationship;
|
||||
import org.neo4j.graphdb.RelationshipType;
|
||||
import org.neo4j.graphdb.index.Index;
|
||||
import org.neo4j.graphdb.traversal.TraversalDescription;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 29.03.11
|
||||
*/
|
||||
public interface GraphDatabase {
|
||||
/**
|
||||
* @return the reference node of the underlying graph database
|
||||
*/
|
||||
Node getReferenceNode();
|
||||
|
||||
/**
|
||||
* @param id node id
|
||||
* @return the requested node of the underlying graph database
|
||||
* @throws org.neo4j.graphdb.NotFoundException
|
||||
*/
|
||||
Node getNodeById(long id);
|
||||
|
||||
/**
|
||||
* Transactionally creates the node, sets the properties (if any).
|
||||
* Two shortcut means of providing the properties (very short with static imports)
|
||||
* <code>graphDatabase.createNode(PropertyMap._("name","value"));</code>
|
||||
* <code>graphDatabase.createNode(PropertyMap.props().set("name","value").set("prop","anotherValue").toMap(), "name", "prop");</code>
|
||||
*
|
||||
* @param props properties to be set at node creation might be null
|
||||
* @return the newly created node
|
||||
*/
|
||||
Node createNode(Property... props);
|
||||
|
||||
/**
|
||||
* @param id relationship id
|
||||
* @return the requested relationship of the underlying graph database
|
||||
* @throws org.neo4j.graphdb.NotFoundException
|
||||
*/
|
||||
Relationship getRelationshipById(long id);
|
||||
|
||||
/**
|
||||
* Transactionally creates the relationship, sets the properties (if any) and indexes the given fielss (if any)
|
||||
* Two shortcut means of providing the properties (very short with static imports)
|
||||
* <code>graphDatabase.createRelationship(from,to,TYPE, PropertyMap._("name","value"));</code>
|
||||
* <code>graphDatabase.createRelationship(from,to,TYPE, PropertyMap.props().set("name","value").set("prop","anotherValue").toMap(), "name", "prop");</code>
|
||||
*
|
||||
*
|
||||
* @param startNode start-node of relationship
|
||||
* @param endNode end-node of relationship
|
||||
* @param type relationship type, might by an enum implementing RelationshipType or a DynamicRelationshipType.withName("name")
|
||||
* @param props optional initial properties
|
||||
* @return the newly created relationship
|
||||
*/
|
||||
Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Property... props);
|
||||
|
||||
/**
|
||||
* @param indexName existing index name, not null
|
||||
* @return existing index {@link Index}
|
||||
* @throws IllegalArgumentException if the index doesn't exist
|
||||
*/
|
||||
<T extends PropertyContainer> Index<T> getIndex(String indexName);
|
||||
|
||||
/**
|
||||
* creates a index
|
||||
* @param type type of index requested - either Node.class or Relationship.class
|
||||
* @param indexName, not null
|
||||
* @param fullText true if a fulltext queryable index is needed, false for exact match
|
||||
* @return node index {@link Index}
|
||||
*/
|
||||
<T extends PropertyContainer> Index<T> createIndex(Class<T> type, String indexName, boolean fullText);
|
||||
|
||||
|
||||
/**
|
||||
* @return a TraversalDescription as starting point for defining a traversal
|
||||
*/
|
||||
TraversalDescription createTraversalDescription();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package org.springframework.data.graph.core;
|
||||
|
||||
import org.neo4j.graphdb.GraphDatabaseService;
|
||||
import org.neo4j.kernel.EmbeddedGraphDatabase;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.data.graph.neo4j.support.DelegatingGraphDatabase;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 25.01.11
|
||||
*/
|
||||
public class GraphDatabaseFactory implements FactoryBean<GraphDatabase> {
|
||||
|
||||
private String storeLocation;
|
||||
private String userName;
|
||||
private String password;
|
||||
protected GraphDatabase graphDatabase;
|
||||
|
||||
public String getStoreLocation() {
|
||||
return storeLocation;
|
||||
}
|
||||
|
||||
public void setStoreLocation(String storeLocation) {
|
||||
this.storeLocation = storeLocation;
|
||||
}
|
||||
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
private GraphDatabase databaseFor(String url, String username, String password) throws Exception {
|
||||
if (url.startsWith( "http://" ) || url.startsWith( "https://" )) {
|
||||
return createRestGraphDatabase(url, username, password);
|
||||
}
|
||||
String path=url;
|
||||
if (url.startsWith( "file:" )) {
|
||||
path = new URI(url).getPath();
|
||||
}
|
||||
File file = new File( path );
|
||||
// if (!file.isDirectory()) file=file.getParentFile();
|
||||
return new DelegatingGraphDatabase(new EmbeddedGraphDatabase(file.getAbsolutePath()));
|
||||
}
|
||||
|
||||
private GraphDatabase createRestGraphDatabase(String url, String username, String password) throws Exception {
|
||||
Class<?> restGraphDatabaseClass = Class.forName("org.neo4j.rest.graphdb.RestGraphDatabase");
|
||||
Constructor<?> constructor = restGraphDatabaseClass.getConstructor(URI.class, String.class, String.class);
|
||||
return (GraphDatabase) constructor.newInstance(new URI(url), username,password );
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphDatabase getObject() throws Exception {
|
||||
if (graphDatabase==null) graphDatabase = databaseFor(storeLocation, userName, password);
|
||||
return graphDatabase;
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void shutdown() {
|
||||
if (graphDatabase instanceof DelegatingGraphDatabase) {
|
||||
((DelegatingGraphDatabase)graphDatabase).shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return GraphDatabaseService.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.springframework.data.graph.core;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 31.03.11
|
||||
*/
|
||||
public class Property {
|
||||
public final String name;
|
||||
public final Object value;
|
||||
|
||||
public Property(String name, Object value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static Property _(String name, Object value) {
|
||||
return new Property(name, value);
|
||||
}
|
||||
|
||||
public static Property[] _(String name1, Object value1, String name2, Object value2) {
|
||||
return new Property[]{new Property(name1, value1), new Property(name2, value2)};
|
||||
}
|
||||
|
||||
public static Property[] _(String name1, Object value1, String name2, Object value2, String name3, Object value3) {
|
||||
return new Property[]{new Property(name1, value1), new Property(name2, value2), new Property(name3, value3)};
|
||||
}
|
||||
|
||||
public static Property[] _(Object... nameValuePairs) {
|
||||
if (nameValuePairs.length % 2 != 0)
|
||||
throw new IllegalArgumentException("there must be an even number of name value pairs");
|
||||
Property[] result = new Property[nameValuePairs.length / 2];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = new Property(nameValuePairs[i * 2].toString(), nameValuePairs[i * 2 + 1]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Property[] _(Map<String, Object> properties) {
|
||||
Property[] result = new Property[properties.size()];
|
||||
int i = 0;
|
||||
for (Map.Entry<String, Object> entry : properties.entrySet()) {
|
||||
result[i++] = new Property(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -31,9 +31,10 @@ import org.neo4j.graphdb.PropertyContainer;
|
||||
*/
|
||||
public interface TypeRepresentationStrategy<S extends PropertyContainer, T extends GraphBacked<S>> {
|
||||
/**
|
||||
* callback on entity creation for setting up type representation
|
||||
* @param state
|
||||
* @param type
|
||||
* Callback for setting up and/or storing type information after creation.
|
||||
*
|
||||
* @param state Backing state of entity being created
|
||||
* @param type Type of entity being created
|
||||
*/
|
||||
void postEntityCreation(S state, Class<? extends T> type);
|
||||
|
||||
@@ -57,10 +58,12 @@ public interface TypeRepresentationStrategy<S extends PropertyContainer, T exten
|
||||
<U extends T> Class<U> getJavaType(S state);
|
||||
|
||||
/**
|
||||
* callback for lifecycle management before node entity removal
|
||||
* @param entity
|
||||
* Callback for cleaning up type information before removal. If state does not have any
|
||||
* state associated, doesn't do anything.
|
||||
*
|
||||
* @param state Backing state of entity being removed
|
||||
*/
|
||||
void preEntityRemoval(T entity);
|
||||
void preEntityRemoval(S state);
|
||||
|
||||
/**
|
||||
* Instantiate the entity given its state. The type of the entity is inferred by the strategy
|
||||
|
||||
@@ -16,10 +16,7 @@
|
||||
|
||||
package org.springframework.data.graph.neo4j.fieldaccess;
|
||||
|
||||
import org.neo4j.graphdb.Direction;
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.Relationship;
|
||||
import org.neo4j.graphdb.RelationshipType;
|
||||
import org.neo4j.graphdb.*;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.graph.core.GraphBacked;
|
||||
import org.springframework.data.graph.neo4j.support.GraphDatabaseContext;
|
||||
@@ -31,7 +28,7 @@ import java.util.Set;
|
||||
* @author Michael Hunger
|
||||
* @since 11.09.2010
|
||||
*/
|
||||
public abstract class AbstractNodeRelationshipFieldAccessor<ENTITY extends GraphBacked,STATE,TARGET extends GraphBacked,TSTATE> implements FieldAccessor<ENTITY> {
|
||||
public abstract class AbstractNodeRelationshipFieldAccessor<ENTITY extends GraphBacked,STATE extends PropertyContainer,TARGET extends GraphBacked,TSTATE extends PropertyContainer> implements FieldAccessor<ENTITY> {
|
||||
protected final RelationshipType type;
|
||||
protected final Direction direction;
|
||||
protected final Class<? extends TARGET> relatedType;
|
||||
|
||||
@@ -46,7 +46,7 @@ public class ConvertingNodePropertyFieldAccessorFactory implements FieldAccessor
|
||||
|
||||
@Override
|
||||
public FieldAccessor<GraphBacked<PropertyContainer>> forField(final Field field) {
|
||||
return new ConvertingNodePropertyFieldAccessor(field, conversionService);
|
||||
return new ConvertingNodePropertyFieldAccessor(conversionService, DelegatingFieldAccessorFactory.getNeo4jPropertyName(field),field.getType());
|
||||
}
|
||||
|
||||
private boolean isSerializableField(final Field field) {
|
||||
@@ -67,8 +67,8 @@ public class ConvertingNodePropertyFieldAccessorFactory implements FieldAccessor
|
||||
public static class ConvertingNodePropertyFieldAccessor extends PropertyFieldAccessorFactory.PropertyFieldAccessor {
|
||||
private final ConversionService conversionService;
|
||||
|
||||
public ConvertingNodePropertyFieldAccessor(final Field field, final ConversionService conversionService) {
|
||||
super(field, conversionService);
|
||||
public ConvertingNodePropertyFieldAccessor(ConversionService conversionService, String propertyName, Class fieldType) {
|
||||
super(conversionService,propertyName,fieldType);
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ public class ConvertingNodePropertyFieldAccessorFactory implements FieldAccessor
|
||||
}
|
||||
|
||||
private Object deserializePropertyValue(final Object value) {
|
||||
return conversionService.convert(value, field.getType());
|
||||
return conversionService.convert(value, fieldType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public class PropertyFieldAccessorFactory implements FieldAccessorFactory<GraphB
|
||||
|
||||
@Override
|
||||
public FieldAccessor<GraphBacked<PropertyContainer>> forField(final Field field) {
|
||||
return new PropertyFieldAccessor(field,conversionService);
|
||||
return new PropertyFieldAccessor(conversionService,DelegatingFieldAccessorFactory.getNeo4jPropertyName(field),field.getType());
|
||||
}
|
||||
|
||||
private boolean isNeo4jPropertyType(final Class<?> fieldType) {
|
||||
@@ -57,12 +57,14 @@ public class PropertyFieldAccessorFactory implements FieldAccessorFactory<GraphB
|
||||
}
|
||||
|
||||
public static class PropertyFieldAccessor implements FieldAccessor<GraphBacked<PropertyContainer>> {
|
||||
protected final Field field;
|
||||
private final ConversionService conversionService;
|
||||
protected final String propertyName;
|
||||
protected final Class<?> fieldType;
|
||||
|
||||
public PropertyFieldAccessor(final Field field, ConversionService conversionService) {
|
||||
this.field = field;
|
||||
public PropertyFieldAccessor(ConversionService conversionService, String propertyName, Class fieldType) {
|
||||
this.conversionService = conversionService;
|
||||
this.propertyName = propertyName;
|
||||
this.fieldType = fieldType;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -74,9 +76,9 @@ public class PropertyFieldAccessorFactory implements FieldAccessorFactory<GraphB
|
||||
public Object setValue(final GraphBacked<PropertyContainer> graphBacked, final Object newVal) {
|
||||
final PropertyContainer propertyContainer = graphBacked.getPersistentState();
|
||||
if (newVal==null) {
|
||||
propertyContainer.removeProperty(getPropertyName());
|
||||
propertyContainer.removeProperty(propertyName);
|
||||
} else {
|
||||
propertyContainer.setProperty(getPropertyName(), newVal);
|
||||
propertyContainer.setProperty(propertyName, newVal);
|
||||
}
|
||||
return newVal;
|
||||
}
|
||||
@@ -87,17 +89,16 @@ public class PropertyFieldAccessorFactory implements FieldAccessorFactory<GraphB
|
||||
}
|
||||
|
||||
protected Object doGetValue(final GraphBacked<PropertyContainer> graphBacked) {
|
||||
Class<?> type = field.getType();
|
||||
Object value = graphBacked.getPersistentState().getProperty(getPropertyName(), getDefaultValue(type));
|
||||
if (value == null || type.isInstance(value)) return value;
|
||||
if (conversionService!=null) {
|
||||
return conversionService.convert(value,type);
|
||||
PropertyContainer element = graphBacked.getPersistentState();
|
||||
if (element.hasProperty(propertyName)) {
|
||||
Object value = element.getProperty(propertyName);
|
||||
if (value == null || fieldType.isInstance(value)) return value;
|
||||
if (conversionService!=null) {
|
||||
return conversionService.convert(value, fieldType);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private String getPropertyName() {
|
||||
return DelegatingFieldAccessorFactory.getNeo4jPropertyName(field);
|
||||
return getDefaultValue(fieldType);
|
||||
}
|
||||
|
||||
private Object getDefaultValue(final Class<?> type) {
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
package org.springframework.data.graph.neo4j.support;
|
||||
|
||||
import org.neo4j.graphdb.*;
|
||||
import org.neo4j.graphdb.index.Index;
|
||||
import org.neo4j.graphdb.index.IndexManager;
|
||||
import org.neo4j.graphdb.traversal.TraversalDescription;
|
||||
import org.neo4j.index.impl.lucene.LuceneIndexImplementation;
|
||||
import org.neo4j.kernel.AbstractGraphDatabase;
|
||||
import org.neo4j.kernel.Traversal;
|
||||
import org.springframework.data.graph.core.GraphDatabase;
|
||||
import org.springframework.data.graph.core.NodeBacked;
|
||||
import org.springframework.data.graph.core.Property;
|
||||
import org.springframework.data.graph.core.RelationshipBacked;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 29.03.11
|
||||
*/
|
||||
public class DelegatingGraphDatabase implements GraphDatabase {
|
||||
|
||||
protected AbstractGraphDatabase delegate;
|
||||
|
||||
public DelegatingGraphDatabase(final AbstractGraphDatabase delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getReferenceNode() {
|
||||
return delegate.getReferenceNode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getNodeById(long id) {
|
||||
return delegate.getNodeById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node createNode(Property... props) {
|
||||
return setProperties(delegate.createNode(), props);
|
||||
}
|
||||
|
||||
private <T extends PropertyContainer> T setProperties(T primitive, Property... properties) {
|
||||
assert primitive != null;
|
||||
if (properties==null || properties.length==0) return primitive;
|
||||
for (Property prop : properties) {
|
||||
if (prop.value==null) {
|
||||
primitive.removeProperty(prop.name);
|
||||
} else {
|
||||
primitive.setProperty(prop.name, prop.value);
|
||||
}
|
||||
}
|
||||
return primitive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Relationship getRelationshipById(long id) {
|
||||
return delegate.getRelationshipById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Property... props) {
|
||||
return setProperties(startNode.createRelationshipTo(endNode,type),props);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends PropertyContainer> Index<T> getIndex(String indexName) {
|
||||
IndexManager indexManager = delegate.index();
|
||||
if (indexManager.existsForNodes(indexName)) return (Index<T>) indexManager.forNodes(indexName);
|
||||
if (indexManager.existsForRelationships(indexName)) return (Index<T>) indexManager.forRelationships(indexName);
|
||||
throw new IllegalArgumentException("Index "+indexName+" does not exist.");
|
||||
}
|
||||
|
||||
// TODO handle existing indexes
|
||||
@Override
|
||||
public <T extends PropertyContainer> Index<T> createIndex(Class<T> type, String indexName, boolean fullText) {
|
||||
IndexManager indexManager = delegate.index();
|
||||
if (isNode(type)) {
|
||||
if (indexManager.existsForNodes(indexName))
|
||||
return (Index<T>) checkAndGetExistingIndex(indexName, fullText, indexManager.forNodes(indexName));
|
||||
return (Index<T>) indexManager.forNodes(indexName, indexConfigFor(fullText));
|
||||
} else {
|
||||
if (indexManager.existsForRelationships(indexName))
|
||||
return (Index<T>) checkAndGetExistingIndex(indexName, fullText, indexManager.forRelationships(indexName));
|
||||
return (Index<T>) indexManager.forRelationships(indexName, indexConfigFor(fullText));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isNode(Class<? extends PropertyContainer> type) {
|
||||
if (type.equals(Node.class)) return true;
|
||||
if (type.equals(Relationship.class)) return false;
|
||||
throw new IllegalArgumentException("Unknown Graph Primitive, neither Node nor Relationship"+type);
|
||||
}
|
||||
|
||||
private <T extends PropertyContainer> Index<T> checkAndGetExistingIndex(final String indexName, boolean fullText, final Index<T> index) {
|
||||
Map<String, String> existingConfig = delegate.index().getConfiguration(index);
|
||||
Map<String, String> config = indexConfigFor(fullText);
|
||||
if (config.equals(existingConfig)) return index;
|
||||
throw new IllegalArgumentException("Setup for index "+indexName+" does not match "+(fullText ? "fulltext":"exact"));
|
||||
}
|
||||
|
||||
private Map<String, String> indexConfigFor(boolean fullText) {
|
||||
return fullText ? LuceneIndexImplementation.FULLTEXT_CONFIG : LuceneIndexImplementation.EXACT_CONFIG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraversalDescription createTraversalDescription() {
|
||||
return Traversal.description();
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
delegate.shutdown();
|
||||
}
|
||||
}
|
||||
@@ -44,19 +44,206 @@ import java.util.Map;
|
||||
*/
|
||||
public class GraphDatabaseContext {
|
||||
|
||||
private static final Log log = LogFactory.getLog(GraphDatabaseContext.class);
|
||||
public static final String DEFAULT_NODE_INDEX_NAME = "node";
|
||||
public static final String DEFAULT_RELATIONSHIP_INDEX_NAME = "relationship";
|
||||
|
||||
private GraphDatabaseService graphDatabaseService;
|
||||
|
||||
private ConversionService conversionService;
|
||||
|
||||
private Validator validator;
|
||||
private NodeTypeRepresentationStrategy nodeTypeRepresentationStrategy;
|
||||
|
||||
private RelationshipTypeRepresentationStrategy relationshipTypeRepresentationStrategy;
|
||||
|
||||
private Validator validator;
|
||||
|
||||
private final static Log log = LogFactory.getLog(GraphDatabaseContext.class);
|
||||
|
||||
public <S extends PropertyContainer, T extends GraphBacked<S>> Index<S> getIndex(Class<T> type) {
|
||||
return getIndex(type, null);
|
||||
}
|
||||
|
||||
public <S extends PropertyContainer, T extends GraphBacked<S>> Index<S> getIndex(Class<T> type, String indexName) {
|
||||
return getIndex(type, indexName, false);
|
||||
}
|
||||
|
||||
|
||||
public <S extends PropertyContainer, T extends GraphBacked<S>> Index<S> getIndex(Class<T> type, String indexName, boolean fullText) {
|
||||
if (indexName==null) indexName = Indexed.Name.get(type);
|
||||
Map<String, String> config = fullText ? LuceneIndexImplementation.FULLTEXT_CONFIG : null;
|
||||
if (NodeBacked.class.isAssignableFrom(type)) return (Index<S>) getIndexManager().forNodes(indexName, config);
|
||||
if (RelationshipBacked.class.isAssignableFrom(type)) return (Index<S>) getIndexManager().forRelationships(indexName, config);
|
||||
throw new IllegalArgumentException("Wrong index type supplied: " + type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if a transaction manager is available and a transaction is currently running
|
||||
*/
|
||||
public boolean transactionIsRunning() {
|
||||
try {
|
||||
return getTxManager().getStatus() != Status.STATUS_NO_TRANSACTION;
|
||||
} catch (SystemException e) {
|
||||
log.error("Error accessing TransactionManager", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public <T extends GraphBacked<? extends PropertyContainer>> Iterable<T> findAll(final Class<T> entityClass) {
|
||||
return getTypeRepresentationStrategy(entityClass).findAll(entityClass);
|
||||
}
|
||||
|
||||
public <T extends GraphBacked<? extends PropertyContainer>> long count(final Class<T> entityClass) {
|
||||
return getTypeRepresentationStrategy(entityClass).count(entityClass);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public <S extends PropertyContainer, T extends GraphBacked<S>> T createEntityFromStoredType(S state) {
|
||||
return getTypeRepresentationStrategy(state).createEntity(state);
|
||||
}
|
||||
|
||||
public <S extends PropertyContainer, T extends GraphBacked<S>> T createEntityFromState(S state, Class<T> type) {
|
||||
if (state==null) throw new IllegalArgumentException("state has to be either a Node or Relationship, not null");
|
||||
return getTypeRepresentationStrategy(state, type).createEntity(state, type);
|
||||
}
|
||||
|
||||
public <S extends PropertyContainer, T extends GraphBacked<S>> T projectTo(GraphBacked<S> entity, Class<T> targetType) {
|
||||
S state = entity.getPersistentState();
|
||||
return getTypeRepresentationStrategy(state, targetType).projectEntity(state, targetType);
|
||||
}
|
||||
|
||||
public <S extends PropertyContainer, T extends GraphBacked<S>> void postEntityCreation(S node, Class<T> entityClass) {
|
||||
getTypeRepresentationStrategy(node, entityClass).postEntityCreation(node, entityClass);
|
||||
}
|
||||
|
||||
|
||||
public void removeNodeEntity(NodeBacked entity) {
|
||||
Node node = entity.getPersistentState();
|
||||
if (node == null) return;
|
||||
nodeTypeRepresentationStrategy.preEntityRemoval(node);
|
||||
for (Relationship relationship : node.getRelationships()) {
|
||||
removeRelationship(relationship);
|
||||
}
|
||||
removeFromIndexes(node);
|
||||
node.delete();
|
||||
}
|
||||
|
||||
public void removeRelationshipEntity(RelationshipBacked entity) {
|
||||
Relationship relationship = entity.getPersistentState();
|
||||
if (relationship == null) return;
|
||||
removeRelationship(relationship);
|
||||
}
|
||||
|
||||
private void removeRelationship(Relationship relationship) {
|
||||
relationshipTypeRepresentationStrategy.preEntityRemoval(relationship);
|
||||
removeFromIndexes(relationship);
|
||||
relationship.delete();
|
||||
}
|
||||
|
||||
private void removeFromIndexes(Node node) {
|
||||
IndexManager indexManager = getIndexManager();
|
||||
for (String indexName : indexManager.nodeIndexNames()) {
|
||||
indexManager.forNodes(indexName).remove(node);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeFromIndexes(Relationship relationship) {
|
||||
IndexManager indexManager = getIndexManager();
|
||||
for (String indexName : indexManager.relationshipIndexNames()) {
|
||||
indexManager.forRelationships(indexName).remove(relationship);
|
||||
}
|
||||
}
|
||||
|
||||
private IndexManager getIndexManager() {
|
||||
return graphDatabaseService.index();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends GraphBacked<? extends PropertyContainer>>
|
||||
TypeRepresentationStrategy<?, T> getTypeRepresentationStrategy(Class<T> type) {
|
||||
if (NodeBacked.class.isAssignableFrom(type)) {
|
||||
return (TypeRepresentationStrategy<?, T>) nodeTypeRepresentationStrategy;
|
||||
} else if (RelationshipBacked.class.isAssignableFrom(type)) {
|
||||
return (TypeRepresentationStrategy<?, T>) relationshipTypeRepresentationStrategy;
|
||||
}
|
||||
throw new IllegalArgumentException("Type is not NodeBacked nor RelationshipBacked.");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <S extends PropertyContainer, T extends GraphBacked<S>>
|
||||
TypeRepresentationStrategy<S, T> getTypeRepresentationStrategy(S state, Class<T> type) {
|
||||
if (state instanceof Node && NodeBacked.class.isAssignableFrom(type)) {
|
||||
return (TypeRepresentationStrategy<S, T>) nodeTypeRepresentationStrategy;
|
||||
} else if (state instanceof Relationship && RelationshipBacked.class.isAssignableFrom(type)) {
|
||||
return (TypeRepresentationStrategy<S, T>) relationshipTypeRepresentationStrategy;
|
||||
}
|
||||
throw new IllegalArgumentException("Type is not NodeBacked nor RelationshipBacked.");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <S extends PropertyContainer, T extends GraphBacked<S>>
|
||||
TypeRepresentationStrategy<S, T> getTypeRepresentationStrategy(S state) {
|
||||
if (state instanceof Node) {
|
||||
return (TypeRepresentationStrategy<S, T>) nodeTypeRepresentationStrategy;
|
||||
} else if (state instanceof Relationship) {
|
||||
return (TypeRepresentationStrategy<S, T>) relationshipTypeRepresentationStrategy;
|
||||
}
|
||||
throw new IllegalArgumentException("Type is not NodeBacked nor RelationshipBacked.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return Neo4j Transaction manager
|
||||
*/
|
||||
public TransactionManager getTxManager() {
|
||||
return ((AbstractGraphDatabase) graphDatabaseService).getConfig().getTxModule().getTxManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to {@link GraphDatabaseService}
|
||||
*/
|
||||
public Node createNode() {
|
||||
return graphDatabaseService.createNode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to {@link GraphDatabaseService}
|
||||
*/
|
||||
public Node getNodeById(final long nodeId) {
|
||||
return graphDatabaseService.getNodeById(nodeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to {@link GraphDatabaseService}
|
||||
*/
|
||||
public Node getReferenceNode() {
|
||||
return graphDatabaseService.getReferenceNode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to {@link GraphDatabaseService}
|
||||
*/
|
||||
public Iterable<? extends Node> getAllNodes() {
|
||||
return graphDatabaseService.getAllNodes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to {@link GraphDatabaseService}
|
||||
*/
|
||||
public Transaction beginTx() {
|
||||
return graphDatabaseService.beginTx();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to {@link GraphDatabaseService}
|
||||
*/
|
||||
public Relationship getRelationshipById(final long id) {
|
||||
return graphDatabaseService.getRelationshipById(id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public GraphDatabaseService getGraphDatabaseService() {
|
||||
return graphDatabaseService;
|
||||
@@ -90,230 +277,6 @@ public class GraphDatabaseContext {
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
public Node createNode() {
|
||||
return graphDatabaseService.createNode();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param relationship to remove from indexes and to delete
|
||||
*/
|
||||
private void removeRelationship(Relationship relationship) {
|
||||
removeFromIndexes(relationship);
|
||||
relationship.delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param relationship to be removed from all indexes, all properties are removed from all indexes
|
||||
*/
|
||||
private void removeFromIndexes(Relationship relationship) {
|
||||
IndexManager indexManager = graphDatabaseService.index();
|
||||
for (String indexName : getIndexManager().relationshipIndexNames()) {
|
||||
indexManager.forRelationships(indexName).remove(relationship);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* removes the entity by cleaning the relationships first and then removing the node
|
||||
* it removes all of them from all indexes in advance
|
||||
* the entity and relationship are still accessible after removal but before transaction commit
|
||||
* but all modifications will throw an exception
|
||||
* @param entity to remove
|
||||
*/
|
||||
// TODO: What about connected relationship entities?
|
||||
public void removeNodeEntity(NodeBacked entity) {
|
||||
Node node = entity.getPersistentState();
|
||||
if (node==null) return;
|
||||
nodeTypeRepresentationStrategy.preEntityRemoval(entity);
|
||||
for (Relationship relationship : node.getRelationships()) {
|
||||
removeRelationship(relationship);
|
||||
}
|
||||
removeFromIndexes(node);
|
||||
node.delete();
|
||||
}
|
||||
|
||||
public void removeRelationshipEntity(RelationshipBacked entity) {
|
||||
Relationship relationship = entity.getPersistentState();
|
||||
if (relationship==null) return;
|
||||
removeRelationship(relationship);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param node to be removed from all indexes, all properties of the node are removed from all indexes
|
||||
*/
|
||||
private void removeFromIndexes(Node node) {
|
||||
IndexManager indexManager = graphDatabaseService.index();
|
||||
for (String indexName : getIndexManager().nodeIndexNames()) {
|
||||
indexManager.forNodes(indexName).remove(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates either a node or relationship entity by delegating the creation to the appropriate @{link EntityInstantiator}
|
||||
* @param state Node or Relationship
|
||||
* @param type target entity type
|
||||
* @return an instance of the entity type
|
||||
*/
|
||||
public <S, T extends GraphBacked> T createEntityFromState(final S state, final Class<T> type) {
|
||||
if (state==null) throw new IllegalArgumentException("state has to be either a Node or Relationship, not null");
|
||||
if (state instanceof Node && NodeBacked.class.isAssignableFrom(type))
|
||||
return (T) nodeTypeRepresentationStrategy.createEntity((Node) state, (Class<NodeBacked>) type);
|
||||
// return (T) graphEntityInstantiator.createEntityFromState((Node) state, typeRepresentationStrategy.confirmType((Node)state, (Class<? extends NodeBacked>)type));
|
||||
else
|
||||
return (T) relationshipTypeRepresentationStrategy.createEntity((Relationship) state, (Class<RelationshipBacked>) type);
|
||||
// return (T) relationshipEntityInstantiator.createEntityFromState((Relationship) state, (Class<? extends RelationshipBacked>) type);
|
||||
}
|
||||
|
||||
private IndexManager getIndexManager() {
|
||||
return graphDatabaseService.index();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param indexName or null, "node" is assumed if null
|
||||
* @return node index {@link Index}
|
||||
*/
|
||||
public <T extends PropertyContainer,N extends GraphBacked<T>> Index<T> getIndex(Class<N> type, String indexName) {
|
||||
if (indexName==null) indexName = Indexed.Name.get(type);
|
||||
if (NodeBacked.class.isAssignableFrom(type)) return (Index<T>) getIndexManager().forNodes(indexName);
|
||||
if (RelationshipBacked.class.isAssignableFrom(type)) return (Index<T>) getIndexManager().forRelationships(indexName);
|
||||
throw new IllegalArgumentException("Wrong index type supplied "+type);
|
||||
}
|
||||
/**
|
||||
* @param type type of index requested - either Node.class or Relationship.class
|
||||
* @param indexName or null, "node" is assumed if null
|
||||
* @param fullText true if a fulltext queryable index is needed, false for exact match
|
||||
* @return node index {@link Index}
|
||||
*/
|
||||
public <T extends PropertyContainer,N extends GraphBacked<T>> Index<T> getIndex(Class<N> type, String indexName, boolean fullText) {
|
||||
if (indexName==null) indexName = Indexed.Name.get(type);
|
||||
Map<String, String> config = fullText ? LuceneIndexImplementation.FULLTEXT_CONFIG : LuceneIndexImplementation.EXACT_CONFIG;
|
||||
if (NodeBacked.class.isAssignableFrom(type)) return (Index<T>) getIndexManager().forNodes(indexName, config);
|
||||
if (RelationshipBacked.class.isAssignableFrom(type)) return (Index<T>) getIndexManager().forRelationships(indexName, config);
|
||||
throw new IllegalArgumentException("Wrong index type supplied "+type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nodeId
|
||||
* @return Node
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public Node getNodeById(final long nodeId) {
|
||||
return graphDatabaseService.getNodeById(nodeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* delegates to the configured @{link TypeRepresentationStrategy} for after entity creation operations
|
||||
* @param node
|
||||
* @param entityClass
|
||||
*/
|
||||
public void postEntityCreation(Node node, final Class<? extends NodeBacked> entityClass) {
|
||||
nodeTypeRepresentationStrategy.postEntityCreation(node, entityClass);
|
||||
}
|
||||
/**
|
||||
* delegates to the configured @{link TypeRepresentationStrategy} for after entity creation operations
|
||||
* @param relationship
|
||||
* @param entityClass
|
||||
*/
|
||||
public void postEntityCreation(Relationship relationship, final Class<? extends RelationshipBacked> entityClass) {
|
||||
relationshipTypeRepresentationStrategy.postEntityCreation(relationship, entityClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* delegates to the configured @{link TypeRepresentationStrategy} to iterate over all instances of this type
|
||||
* @param clazz type of entity
|
||||
* @param <T>
|
||||
* @return
|
||||
* TODO inheritance handling
|
||||
*/
|
||||
public <T extends GraphBacked> Iterable<T> findAll(final Class<T> clazz) {
|
||||
if (!checkIsNodeBacked(clazz)) throw new UnsupportedOperationException("No support for relationships");
|
||||
return (Iterable<T>) nodeTypeRepresentationStrategy.findAll((Class<NodeBacked>)clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* class base check for nodebacked subclasses
|
||||
*/
|
||||
private boolean checkIsNodeBacked(Class<?> clazz) {
|
||||
return NodeBacked.class.isAssignableFrom(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* delegates to the configured @{link TypeRepresentationStrategy} for a count of all instances of this type
|
||||
* @param entityClass
|
||||
* @return count of all instances
|
||||
*/
|
||||
public long count(final Class<? extends GraphBacked> entityClass) {
|
||||
if (!checkIsNodeBacked(entityClass)) throw new UnsupportedOperationException("No support for relationships");
|
||||
return nodeTypeRepresentationStrategy.count((Class<NodeBacked>)entityClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* delegates to the configured @{link TypeRepresentationStrategy} to lookup the type information for the given node
|
||||
* @param node
|
||||
* @param <T>
|
||||
* @return entity type of the node
|
||||
* @throws IllegalStateException for nodes that are not instance backing nodes of a known type
|
||||
*/
|
||||
public <T extends NodeBacked> Class<T> getJavaType(final Node node) {
|
||||
return nodeTypeRepresentationStrategy.getJavaType(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return reference node of the graph database
|
||||
*/
|
||||
public Node getReferenceNode() {
|
||||
return graphDatabaseService.getReferenceNode();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Neo4j Transaction manager
|
||||
*/
|
||||
public TransactionManager getTxManager() {
|
||||
|
||||
return ((AbstractGraphDatabase) graphDatabaseService).getConfig().getTxModule().getTxManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if a transaction manager is available and a transaction is currently running
|
||||
*/
|
||||
public boolean transactionIsRunning() {
|
||||
try {
|
||||
return getTxManager().getStatus() != Status.STATUS_NO_TRANSACTION;
|
||||
} catch (SystemException e) {
|
||||
log.error("Error accessing TransactionManager", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* delegates to @{link GraphDatabaseService}
|
||||
*/
|
||||
public Iterable<? extends Node> getAllNodes() {
|
||||
return graphDatabaseService.getAllNodes();
|
||||
}
|
||||
|
||||
/**
|
||||
* delegates to @{link GraphDatabaseService}
|
||||
*/
|
||||
public Transaction beginTx() {
|
||||
return graphDatabaseService.beginTx();
|
||||
}
|
||||
|
||||
/**
|
||||
* delegates to @{link GraphDatabaseService}
|
||||
*/
|
||||
public Relationship getRelationshipById(final long id) {
|
||||
return graphDatabaseService.getRelationshipById(id);
|
||||
}
|
||||
|
||||
public <T extends GraphBacked> T projectTo(GraphBacked entity, Class<T> targetType) {
|
||||
final Object state = entity.getPersistentState();
|
||||
if (state instanceof Node)
|
||||
return (T) nodeTypeRepresentationStrategy.projectEntity((Node) state, (Class<NodeBacked>) targetType);
|
||||
else
|
||||
return (T) relationshipTypeRepresentationStrategy.projectEntity((Relationship) state, (Class<RelationshipBacked>) targetType);
|
||||
}
|
||||
|
||||
public Validator getValidator() {
|
||||
return validator;
|
||||
}
|
||||
@@ -322,8 +285,7 @@ public class GraphDatabaseContext {
|
||||
this.validator = validatorFactory;
|
||||
}
|
||||
|
||||
public <T extends NodeBacked> T createEntityFromStoredType(Node node) {
|
||||
return nodeTypeRepresentationStrategy.createEntity(node);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,6 @@ public class IndexingNodeTypeRepresentationStrategy implements NodeTypeRepresent
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Class<? extends NodeBacked> getJavaType(Node node) {
|
||||
if (node == null) throw new IllegalArgumentException("Node is null");
|
||||
@@ -112,8 +111,8 @@ public class IndexingNodeTypeRepresentationStrategy implements NodeTypeRepresent
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preEntityRemoval(NodeBacked entity) {
|
||||
getNodeTypesIndex().remove(entity.getPersistentState());
|
||||
public void preEntityRemoval(Node state) {
|
||||
getNodeTypesIndex().remove(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -116,10 +116,11 @@ public class IndexingRelationshipTypeRepresentationStrategy implements Relations
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void preEntityRemoval(RelationshipBacked entity) {
|
||||
getRelTypesIndex().remove(entity.getPersistentState());
|
||||
}
|
||||
public void preEntityRemoval(Relationship state) {
|
||||
getRelTypesIndex().remove(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.springframework.data.graph.neo4j.support;
|
||||
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.springframework.data.graph.core.NodeBacked;
|
||||
import org.springframework.data.graph.core.NodeTypeRepresentationStrategy;
|
||||
|
||||
public class NoopNodeTypeRepresentationStrategy implements NodeTypeRepresentationStrategy {
|
||||
|
||||
@Override
|
||||
public void postEntityCreation(Node state, Class<? extends NodeBacked> type) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U extends NodeBacked> Iterable<U> findAll(Class<U> clazz) {
|
||||
throw new UnsupportedOperationException("findAll not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(Class<? extends NodeBacked> entityClass) {
|
||||
throw new UnsupportedOperationException("count not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preEntityRemoval(Node state) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends NodeBacked> getJavaType(Node state) {
|
||||
throw new UnsupportedOperationException("getJavaType not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U extends NodeBacked> U createEntity(Node state) {
|
||||
throw new UnsupportedOperationException("Creation with stored type not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U extends NodeBacked> U createEntity(Node state, Class<U> type) {
|
||||
return projectEntity(state, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U extends NodeBacked> U projectEntity(Node state, Class<U> type) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.springframework.data.graph.neo4j.support;
|
||||
|
||||
import org.neo4j.graphdb.Relationship;
|
||||
import org.springframework.data.graph.core.RelationshipBacked;
|
||||
import org.springframework.data.graph.core.RelationshipTypeRepresentationStrategy;
|
||||
|
||||
public class NoopRelationshipTypeRepresentationStrategy implements RelationshipTypeRepresentationStrategy {
|
||||
|
||||
@Override
|
||||
public void postEntityCreation(Relationship state, Class<? extends RelationshipBacked> type) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U extends RelationshipBacked> Iterable<U> findAll(Class<U> clazz) {
|
||||
throw new UnsupportedOperationException("findAll not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(Class<? extends RelationshipBacked> entityClass) {
|
||||
throw new UnsupportedOperationException("count not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preEntityRemoval(Relationship state) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends RelationshipBacked> getJavaType(Relationship state) {
|
||||
throw new UnsupportedOperationException("getJavaType not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U extends RelationshipBacked> U createEntity(Relationship state) {
|
||||
throw new UnsupportedOperationException("Creation with stored type not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U extends RelationshipBacked> U createEntity(Relationship state, Class<U> type) {
|
||||
return projectEntity(state, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U extends RelationshipBacked> U projectEntity(Relationship state, Class<U> type) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
package org.springframework.data.graph.neo4j.support;
|
||||
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.Relationship;
|
||||
import org.springframework.data.graph.core.NodeBacked;
|
||||
import org.springframework.data.graph.core.NodeTypeRepresentationStrategy;
|
||||
import org.springframework.data.graph.core.RelationshipBacked;
|
||||
import org.springframework.data.graph.core.RelationshipTypeRepresentationStrategy;
|
||||
|
||||
public class NoopTypeRepresentationStrategy {
|
||||
public static class NoopNodeStrategy implements NodeTypeRepresentationStrategy {
|
||||
|
||||
@Override
|
||||
public void postEntityCreation(Node state, Class<? extends NodeBacked> type) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U extends NodeBacked> Iterable<U> findAll(Class<U> clazz) {
|
||||
throw new UnsupportedOperationException("findAll not supported by NoopTypeRepresentationStrategy.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(Class<? extends NodeBacked> entityClass) {
|
||||
throw new UnsupportedOperationException("count not supported by NoopTypeRepresentationStrategy.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends NodeBacked> getJavaType(Node state) {
|
||||
throw new UnsupportedOperationException("getJavaType not supported by NoopTypeRepresentationStrategy.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preEntityRemoval(NodeBacked entity) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U extends NodeBacked> U createEntity(Node state) {
|
||||
throw new UnsupportedOperationException("Creation with stored type not supported by NoopTypeRepresentationStrategy.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U extends NodeBacked> U createEntity(Node state, Class<U> type) {
|
||||
return projectEntity(state, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U extends NodeBacked> U projectEntity(Node state, Class<U> type) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class NoopRelationshipStrategy implements RelationshipTypeRepresentationStrategy {
|
||||
|
||||
@Override
|
||||
public void postEntityCreation(Relationship state, Class<? extends RelationshipBacked> type) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U extends RelationshipBacked> Iterable<U> findAll(Class<U> clazz) {
|
||||
throw new UnsupportedOperationException("findAll not supported by NoopTypeRepresentationStrategy.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(Class<? extends RelationshipBacked> entityClass) {
|
||||
throw new UnsupportedOperationException("count not supported by NoopTypeRepresentationStrategy.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends RelationshipBacked> getJavaType(Relationship state) {
|
||||
throw new UnsupportedOperationException("getJavaType not supported by NoopTypeRepresentationStrategy.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preEntityRemoval(RelationshipBacked entity) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U extends RelationshipBacked> U createEntity(Relationship state) {
|
||||
throw new UnsupportedOperationException("Creation with stored type not supported by NoopTypeRepresentationStrategy.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U extends RelationshipBacked> U createEntity(Relationship state, Class<U> type) {
|
||||
return projectEntity(state, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U extends RelationshipBacked> U projectEntity(Relationship state, Class<U> type) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,34 +100,6 @@ public class SubReferenceNodeTypeRepresentationStrategy implements NodeTypeRepre
|
||||
updateSuperClassSubrefs(type, subReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* removes instanceof relationship and decrements instance counters for type nodes
|
||||
* @param entity
|
||||
*/
|
||||
@Override
|
||||
public void preEntityRemoval(NodeBacked entity) {
|
||||
Class<? extends NodeBacked> clazz = entity.getClass();
|
||||
|
||||
final Node subReference = obtainSubreferenceNode(clazz);
|
||||
Node subRefNode = entity.getPersistentState();
|
||||
Relationship instanceOf = subRefNode.getSingleRelationship(INSTANCE_OF_RELATIONSHIP_TYPE, Direction.OUTGOING);
|
||||
instanceOf.delete();
|
||||
if (log.isDebugEnabled()) log.debug("Removed link to subref node: " + subReference + " with type: " + clazz.getName());
|
||||
TraversalDescription traversal = Traversal.description().depthFirst().relationships(SUBCLASS_OF_RELATIONSHIP_TYPE, Direction.OUTGOING);
|
||||
for (Node node : traversal.traverse(subReference).nodes()) {
|
||||
Integer count = (Integer) node.getProperty(SUBREFERENCE_NODE_COUNTER_KEY);
|
||||
Integer newCount = decrementAndGetCounter(node, SUBREFERENCE_NODE_COUNTER_KEY, 0);
|
||||
if (log.isDebugEnabled()) log.debug("count on ref " + node + " was " + count + " new " + newCount);
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public <T extends NodeBacked> Class<T> confirmType(Node node, Class<T> type) {
|
||||
// Class<T> nodeType = this.<T>getJavaType(node);
|
||||
// if (type.isAssignableFrom(nodeType)) return nodeType;
|
||||
// throw new IllegalArgumentException(String.format("%s does not correspond to the node type %s of node %s",type,nodeType,node));
|
||||
// }
|
||||
|
||||
private void updateSuperClassSubrefs(Class<?> clazz, Node subReference) {
|
||||
Class<?> superClass = clazz.getSuperclass();
|
||||
if (superClass != null) {
|
||||
@@ -165,7 +137,24 @@ public class SubReferenceNodeTypeRepresentationStrategy implements NodeTypeRepre
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public void preEntityRemoval(Node state) {
|
||||
Class<? extends NodeBacked> clazz = getJavaType(state);
|
||||
if (clazz == null) return;
|
||||
final Node subReference = obtainSubreferenceNode(clazz);
|
||||
Relationship instanceOf = state.getSingleRelationship(INSTANCE_OF_RELATIONSHIP_TYPE, Direction.OUTGOING);
|
||||
instanceOf.delete();
|
||||
if (log.isDebugEnabled())
|
||||
log.debug("Removed link to subref node: " + subReference + " with type: " + clazz.getName());
|
||||
TraversalDescription traversal = Traversal.description().depthFirst().relationships(SUBCLASS_OF_RELATIONSHIP_TYPE, Direction.OUTGOING);
|
||||
for (Node node : traversal.traverse(subReference).nodes()) {
|
||||
Integer count = (Integer) node.getProperty(SUBREFERENCE_NODE_COUNTER_KEY);
|
||||
Integer newCount = decrementAndGetCounter(node, SUBREFERENCE_NODE_COUNTER_KEY, 0);
|
||||
if (log.isDebugEnabled()) log.debug("count on ref " + node + " was " + count + " new " + newCount);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends NodeBacked> Iterable<T> findAll(final Class<T> clazz) {
|
||||
final Node subrefNode = findSubreferenceNode(clazz);
|
||||
if (log.isDebugEnabled()) log.debug("Subref: " + subrefNode);
|
||||
|
||||
@@ -57,7 +57,7 @@ public class TypeRepresentationStrategyFactory {
|
||||
|
||||
@Override
|
||||
public RelationshipTypeRepresentationStrategy getRelationshipTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator) {
|
||||
return new NoopTypeRepresentationStrategy.NoopRelationshipStrategy();
|
||||
return new NoopRelationshipTypeRepresentationStrategy();
|
||||
}
|
||||
},
|
||||
Indexed {
|
||||
@@ -74,12 +74,12 @@ public class TypeRepresentationStrategyFactory {
|
||||
Noop {
|
||||
@Override
|
||||
public NodeTypeRepresentationStrategy getNodeTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator<NodeBacked, Node> graphEntityInstantiator) {
|
||||
return new NoopTypeRepresentationStrategy.NoopNodeStrategy();
|
||||
return new NoopNodeTypeRepresentationStrategy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RelationshipTypeRepresentationStrategy getRelationshipTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator) {
|
||||
return new NoopTypeRepresentationStrategy.NoopRelationshipStrategy();
|
||||
return new NoopRelationshipTypeRepresentationStrategy();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.springframework.data.graph.neo4j.template;
|
||||
|
||||
import org.neo4j.graphdb.*;
|
||||
import org.neo4j.graphdb.traversal.TraversalDescription;
|
||||
import org.springframework.data.graph.core.Property;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -40,11 +41,12 @@ public interface Neo4jOperations {
|
||||
* Two shortcut means of providing the properties (very short with static imports)
|
||||
* <code>template.createNode(PropertyMap._("name","value"));</code>
|
||||
* <code>template.createNode(PropertyMap.props().set("name","value").set("prop","anotherValue").toMap(), "name", "prop");</code>
|
||||
*
|
||||
*
|
||||
* @param props properties to be set at node creation might be null
|
||||
* @param indexFields fields that are automatically indexed from the given properties for the newly created ndoe
|
||||
* @return the newly created node
|
||||
*/
|
||||
Node createNode(Map<String, Object> props, String... indexFields);
|
||||
Node createNode(Property... props);
|
||||
|
||||
/**
|
||||
* Delegates to the GraphDatabaseService
|
||||
@@ -59,14 +61,15 @@ public interface Neo4jOperations {
|
||||
* Two shortcut means of providing the properties (very short with static imports)
|
||||
* <code>template.createRelationship(from,to,TYPE, PropertyMap._("name","value"));</code>
|
||||
* <code>template.createRelationship(from,to,TYPE, PropertyMap.props().set("name","value").set("prop","anotherValue").toMap(), "name", "prop");</code>
|
||||
*
|
||||
*
|
||||
* @param startNode start-node of relationship
|
||||
* @param endNode end-node of relationship
|
||||
* @param type relationship type, might by an enum implementing RelationshipType or a DynamicRelationshipType.withName("name")
|
||||
* @param props optional initial properties
|
||||
* @param indexFields optional indexed fields
|
||||
* @return the newly created relationship
|
||||
*/
|
||||
Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Map<String, Object> props, String... indexFields);
|
||||
Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Property... props);
|
||||
|
||||
/**
|
||||
* Queries the supplied index with a lucene query string or query object (if the neo4j-index provider is lucene)
|
||||
@@ -144,14 +147,4 @@ public interface Neo4jOperations {
|
||||
* @return the provided element for convenience
|
||||
*/
|
||||
<T extends PropertyContainer> T index(String indexName, T element, String field, Object value);
|
||||
/**
|
||||
* Auto-indexes all indexFields for the given element's properties if they exist
|
||||
* @param indexName Name of the index, will be checked against existing indexes according to the given element
|
||||
* assumes a "node" node index or "relationship" relationship index for a null value
|
||||
* @param element node or relationship to auto-index
|
||||
* @param indexProperties property names to index
|
||||
* @param <T> the provided element type
|
||||
* @return the provided element for convenience
|
||||
*/
|
||||
<T extends PropertyContainer> T autoIndex(String indexName, T element, String... indexProperties);
|
||||
}
|
||||
|
||||
@@ -25,8 +25,7 @@ import org.neo4j.helpers.collection.IterableWrapper;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.graph.UncategorizedGraphStoreException;
|
||||
|
||||
import java.util.Map;
|
||||
import org.springframework.data.graph.core.Property;
|
||||
|
||||
public class Neo4jTemplate implements Neo4jOperations {
|
||||
|
||||
@@ -117,13 +116,13 @@ public class Neo4jTemplate implements Neo4jOperations {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node createNode(final Map<String, Object> properties, final String... indexFields) {
|
||||
public Node createNode(final Property... properties) {
|
||||
return exec(new GraphCallback<Node>() {
|
||||
@Override
|
||||
public Node doWithGraph(GraphDatabaseService graph) throws Exception {
|
||||
Node node = graphDatabaseService.createNode();
|
||||
if (properties == null) return node;
|
||||
return autoIndex(null, setProperties(node, properties), indexFields);
|
||||
return setProperties(node, properties);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -148,15 +147,6 @@ public class Neo4jTemplate implements Neo4jOperations {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends PropertyContainer> T autoIndex(String indexName, T element, String... indexFields) {
|
||||
for (String indexField : indexFields) {
|
||||
if (!element.hasProperty(indexField)) continue;
|
||||
index(indexName,element, indexField,element.getProperty(indexField));
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends PropertyContainer> T index(final String indexName, final T element, final String field, final Object value) {
|
||||
notNull(element, "element", field, "field", value, "value");
|
||||
@@ -290,26 +280,26 @@ public class Neo4jTemplate implements Neo4jOperations {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Relationship createRelationship(final Node startNode, final Node endNode, final RelationshipType relationshipType, final Map<String, Object> properties, final String... indexFields) {
|
||||
public Relationship createRelationship(final Node startNode, final Node endNode, final RelationshipType relationshipType, final Property... properties) {
|
||||
notNull(startNode, "startNode", endNode, "endNode", relationshipType, "relationshipType", properties, "properties");
|
||||
return exec(new GraphCallback<Relationship>() {
|
||||
@Override
|
||||
public Relationship doWithGraph(GraphDatabaseService graph) throws Exception {
|
||||
Relationship relationship = startNode.createRelationshipTo(endNode, relationshipType);
|
||||
if (properties == null) return relationship;
|
||||
return autoIndex("relationship", setProperties(relationship, properties), indexFields);
|
||||
return setProperties(relationship, properties);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private <T extends PropertyContainer> T setProperties(T primitive, Map<String, Object> properties) {
|
||||
private <T extends PropertyContainer> T setProperties(T primitive, Property... properties) {
|
||||
assert primitive != null;
|
||||
if (properties==null) return primitive;
|
||||
for (Map.Entry<String, Object> prop : properties.entrySet()) {
|
||||
if (prop.getValue()==null) {
|
||||
primitive.removeProperty(prop.getKey());
|
||||
for (Property prop : properties) {
|
||||
if (prop.value==null) {
|
||||
primitive.removeProperty(prop.name);
|
||||
} else {
|
||||
primitive.setProperty(prop.getKey(), prop.getValue());
|
||||
primitive.setProperty(prop.name, prop.value);
|
||||
}
|
||||
}
|
||||
return primitive;
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.springframework.data.graph.core;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.data.graph.neo4j.support.DelegatingGraphDatabase;
|
||||
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.hamcrest.core.IsInstanceOf.instanceOf;
|
||||
import static org.hamcrest.core.IsNot.not;
|
||||
import static org.hamcrest.core.IsNull.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 29.03.11
|
||||
*/
|
||||
public class GraphDatabaseFactoryTest {
|
||||
|
||||
@Test
|
||||
public void shouldCreateLocalDatabaseFromContext() throws Exception {
|
||||
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("GraphDatabaseFactory-context.xml");
|
||||
try {
|
||||
GraphDatabase graphDatabase = ctx.getBean("graphDatabase", GraphDatabase.class);
|
||||
assertThat(graphDatabase, is(not(nullValue())));
|
||||
assertThat(graphDatabase, is(instanceOf(DelegatingGraphDatabase.class)));
|
||||
} finally {
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
}
|
||||
@Test
|
||||
public void shouldCreateLocalDatabase() throws Exception {
|
||||
GraphDatabaseFactory factory = new GraphDatabaseFactory();
|
||||
try {
|
||||
factory.setStoreLocation("target/test-db");
|
||||
GraphDatabase graphDatabase = factory.getObject();
|
||||
assertThat(graphDatabase, is(not(nullValue())));
|
||||
assertThat(graphDatabase,is(instanceOf(DelegatingGraphDatabase.class)));
|
||||
} finally {
|
||||
factory.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package org.springframework.data.graph.neo4j;
|
||||
|
||||
import org.springframework.data.graph.neo4j.repository.RelationshipGraphRepository;
|
||||
|
||||
public interface FriendshipRepository extends RelationshipGraphRepository<Friendship> {
|
||||
}
|
||||
@@ -2,26 +2,20 @@ package org.springframework.data.graph.neo4j.support;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.Assert;
|
||||
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.domain.Sort;
|
||||
import org.springframework.data.graph.neo4j.Group;
|
||||
import org.springframework.data.graph.neo4j.GroupRepository;
|
||||
import org.springframework.data.graph.neo4j.Person;
|
||||
import org.springframework.data.graph.neo4j.PersonRepository;
|
||||
import org.springframework.data.graph.neo4j.*;
|
||||
import org.springframework.data.graph.neo4j.support.node.Neo4jHelper;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.transaction.BeforeTransaction;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
@@ -34,8 +28,7 @@ import static org.springframework.data.graph.neo4j.Person.persistedPerson;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = {"classpath:org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml"})
|
||||
|
||||
public class FinderTest {
|
||||
public class GraphRepositoryTest {
|
||||
|
||||
protected final Log log = LogFactory.getLog(getClass());
|
||||
|
||||
@@ -46,6 +39,8 @@ public class FinderTest {
|
||||
private PersonRepository personRepository;
|
||||
@Autowired
|
||||
private GroupRepository groupRepository;
|
||||
@Autowired
|
||||
private FriendshipRepository friendshipRepository;
|
||||
|
||||
@BeforeTransaction
|
||||
public void cleanDb() {
|
||||
@@ -78,7 +73,7 @@ public class FinderTest {
|
||||
Person p1 = new Person("Michael", 35);
|
||||
personRepository.save(p1);
|
||||
assertEquals("persisted person",true,p1.hasPersistentState());
|
||||
assertThat(personRepository.findOne(p1.getId()),is(p1));
|
||||
assertThat(personRepository.findOne(p1.getId()), is(p1));
|
||||
}
|
||||
@Test
|
||||
public void testDeletePerson() {
|
||||
@@ -94,6 +89,17 @@ public class FinderTest {
|
||||
assertEquals("people deleted", false, personRepository.findAll().iterator().hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void testFindRelationshipEntity() {
|
||||
Person p1 = persistedPerson("Michael", 35);
|
||||
Person p2 = persistedPerson("David", 27);
|
||||
Friendship friendship = p1.knows(p2);
|
||||
assertEquals("Wrong friendship count.", 1L, (long) friendshipRepository.count());
|
||||
assertEquals("Did not find friendship.", Collections.singleton(friendship), new HashSet<Friendship>(IteratorUtil.asCollection(friendshipRepository.findAll())));
|
||||
assertEquals(friendship, friendshipRepository.findOne(friendship.getRelationshipId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void testFinderFindById() {
|
||||
@@ -78,7 +78,7 @@ public class IndexingNodeTypeRepresentationStrategyTest {
|
||||
Transaction tx = graphDatabaseService.beginTx();
|
||||
try
|
||||
{
|
||||
nodeTypeRepresentationStrategy.preEntityRemoval(thing);
|
||||
nodeTypeRepresentationStrategy.preEntityRemoval(node(thing));
|
||||
tx.success();
|
||||
}
|
||||
finally
|
||||
@@ -92,9 +92,8 @@ public class IndexingNodeTypeRepresentationStrategyTest {
|
||||
assertEquals(node(subThing), subThingHits.getSingle());
|
||||
|
||||
tx = graphDatabaseService.beginTx();
|
||||
try
|
||||
{
|
||||
nodeTypeRepresentationStrategy.preEntityRemoval(subThing);
|
||||
try {
|
||||
nodeTypeRepresentationStrategy.preEntityRemoval(node(subThing));
|
||||
tx.success();
|
||||
}
|
||||
finally
|
||||
|
||||
@@ -74,7 +74,7 @@ public class IndexingRelationshipTypeRepresentationStrategyTest {
|
||||
Transaction tx = graphDatabaseService.beginTx();
|
||||
try
|
||||
{
|
||||
relationshipTypeRepresentationStrategy.preEntityRemoval(link);
|
||||
relationshipTypeRepresentationStrategy.preEntityRemoval(rel(link));
|
||||
tx.success();
|
||||
}
|
||||
finally
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
package org.springframework.data.graph.neo4j.support;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.neo4j.graphdb.DynamicRelationshipType;
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.Relationship;
|
||||
import org.neo4j.graphdb.Transaction;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.graph.annotation.NodeEntity;
|
||||
import org.springframework.data.graph.annotation.RelationshipEntity;
|
||||
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
@@ -11,77 +19,109 @@ import org.springframework.test.context.transaction.TransactionalTestExecutionLi
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = {"classpath:org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml",
|
||||
"classpath:org/springframework/data/graph/neo4j/support/NoopNodeTypeStrategyOverride-context.xml"})
|
||||
"classpath:org/springframework/data/graph/neo4j/support/NoopTypeRepresentationStrategyOverride-context.xml"})
|
||||
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
|
||||
public class NoopTypeRepresentationStrategyTest {
|
||||
//
|
||||
// @Autowired
|
||||
// private GraphDatabaseContext graphDatabaseContext;
|
||||
// @Autowired
|
||||
// private NoopTypeRepresentationStrategy nodeTypeStrategy;
|
||||
//
|
||||
// private Thing thing;
|
||||
//
|
||||
// @Before
|
||||
// public void setUp() throws Exception {
|
||||
// thing = createThing();
|
||||
// }
|
||||
|
||||
@Autowired
|
||||
private GraphDatabaseContext graphDatabaseContext;
|
||||
@Autowired
|
||||
private NoopNodeTypeRepresentationStrategy noopNodeStrategy;
|
||||
@Autowired
|
||||
private NoopRelationshipTypeRepresentationStrategy noopRelationshipStrategy;
|
||||
|
||||
private Thing thing;
|
||||
private Link link;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
createThing();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPostEntityCreation() throws Exception {
|
||||
}
|
||||
//
|
||||
// @Test(expected = UnsupportedOperationException.class)
|
||||
// public void testFindAll() throws Exception {
|
||||
// nodeTypeStrategy.findAll(Thing.class);
|
||||
// }
|
||||
//
|
||||
// @Test(expected = UnsupportedOperationException.class)
|
||||
// public void testCount() throws Exception {
|
||||
// nodeTypeStrategy.count(Thing.class);
|
||||
// }
|
||||
//
|
||||
// @Test(expected = UnsupportedOperationException.class)
|
||||
// public void testGetJavaType() throws Exception {
|
||||
// nodeTypeStrategy.getJavaType(node(thing));
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testPreEntityRemoval() throws Exception {
|
||||
// nodeTypeStrategy.preEntityRemoval(thing);
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testConfirmType() throws Exception {
|
||||
// assertEquals(Thing.class, nodeTypeStrategy.confirmType(node(thing), Thing.class));
|
||||
// }
|
||||
//
|
||||
// private static Node node(Thing thing) {
|
||||
// return thing.getPersistentState();
|
||||
// }
|
||||
//
|
||||
// private Thing createThing() {
|
||||
// Transaction tx = graphDatabaseContext.beginTx();
|
||||
// try {
|
||||
// Node node = graphDatabaseContext.createNode();
|
||||
// Thing thing = new Thing(node);
|
||||
// nodeTypeStrategy.postEntityCreation(thing);
|
||||
// tx.success();
|
||||
// return thing;
|
||||
// } finally {
|
||||
// tx.finish();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @NodeEntity
|
||||
// public static class Thing {
|
||||
// String name;
|
||||
//
|
||||
// public Thing() {
|
||||
// }
|
||||
//
|
||||
// public Thing(Node n) {
|
||||
// setPersistentState(n);
|
||||
// }
|
||||
// }
|
||||
|
||||
@Test(expected = UnsupportedOperationException.class)
|
||||
public void testFindAllForNodeStrategy() throws Exception {
|
||||
noopNodeStrategy.findAll(Thing.class);
|
||||
}
|
||||
|
||||
@Test(expected = UnsupportedOperationException.class)
|
||||
public void testFindAllForRelationshipStrategy() throws Exception {
|
||||
noopRelationshipStrategy.findAll(Link.class);
|
||||
}
|
||||
|
||||
@Test(expected = UnsupportedOperationException.class)
|
||||
public void testCountForNodeStrategy() throws Exception {
|
||||
noopNodeStrategy.count(Thing.class);
|
||||
}
|
||||
|
||||
@Test(expected = UnsupportedOperationException.class)
|
||||
public void testCountForRelationshipStrategy() throws Exception {
|
||||
noopRelationshipStrategy.count(Link.class);
|
||||
}
|
||||
|
||||
@Test(expected = UnsupportedOperationException.class)
|
||||
public void testGetJavaTypeOnNodeStrategy() throws Exception {
|
||||
noopNodeStrategy.getJavaType(null);
|
||||
}
|
||||
|
||||
@Test(expected = UnsupportedOperationException.class)
|
||||
public void testGetJavaTypeOnRelationshipStrategy() throws Exception {
|
||||
noopRelationshipStrategy.getJavaType(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreEntityRemoval() throws Exception {
|
||||
noopNodeStrategy.preEntityRemoval(node(thing));
|
||||
noopRelationshipStrategy.preEntityRemoval(rel(link));
|
||||
}
|
||||
|
||||
private static Node node(Thing thing) {
|
||||
return thing.getPersistentState();
|
||||
}
|
||||
|
||||
private static Relationship rel(Link link) {
|
||||
return link.getPersistentState();
|
||||
}
|
||||
|
||||
private Thing createThing() {
|
||||
Transaction tx = graphDatabaseContext.beginTx();
|
||||
try {
|
||||
Node node = graphDatabaseContext.createNode();
|
||||
thing = new Thing(node);
|
||||
noopNodeStrategy.postEntityCreation(node, Thing.class);
|
||||
Relationship rel = node.createRelationshipTo(graphDatabaseContext.createNode(), DynamicRelationshipType.withName("link"));
|
||||
link = new Link(rel);
|
||||
noopRelationshipStrategy.postEntityCreation(rel, Link.class);
|
||||
tx.success();
|
||||
return thing;
|
||||
} finally {
|
||||
tx.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@NodeEntity
|
||||
public static class Thing {
|
||||
String name;
|
||||
|
||||
public Thing() {
|
||||
}
|
||||
|
||||
public Thing(Node n) {
|
||||
setPersistentState(n);
|
||||
}
|
||||
}
|
||||
|
||||
@RelationshipEntity
|
||||
public static class Link {
|
||||
|
||||
public Link() {
|
||||
}
|
||||
|
||||
public Link(Relationship rel) {
|
||||
setPersistentState(rel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,10 @@ import org.neo4j.graphdb.*;
|
||||
import org.neo4j.helpers.collection.IteratorUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.graph.neo4j.Friendship;
|
||||
import org.springframework.data.graph.neo4j.FriendshipRepository;
|
||||
import org.springframework.data.graph.neo4j.Person;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.springframework.data.graph.neo4j.Person.persistedPerson;
|
||||
|
||||
import org.springframework.data.graph.neo4j.repository.DirectGraphRepositoryFactory;
|
||||
@@ -30,6 +33,10 @@ public class RelationshipEntityTest {
|
||||
|
||||
@Autowired
|
||||
private GraphDatabaseContext graphDatabaseContext;
|
||||
@Autowired
|
||||
private GraphDatabaseService graphDatabaseService;
|
||||
@Autowired
|
||||
private FriendshipRepository friendshipRepository;
|
||||
|
||||
@Autowired
|
||||
private DirectGraphRepositoryFactory graphRepositoryFactory;
|
||||
@@ -97,6 +104,64 @@ public class RelationshipEntityTest {
|
||||
Person p = persistedPerson("Michael", 35);
|
||||
Person p2 = persistedPerson("David", 25);
|
||||
Friendship f = p.knows(p2);
|
||||
assertEquals(f,p.getRelationshipTo(p2,Friendship.class, "knows"));
|
||||
assertEquals(f,p.getRelationshipTo(p2, Friendship.class, "knows"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveRelationshipEntity() {
|
||||
cleanDb();
|
||||
Friendship f;
|
||||
Transaction tx = graphDatabaseService.beginTx();
|
||||
try
|
||||
{
|
||||
Person p = persistedPerson("Michael", 35);
|
||||
Person p2 = persistedPerson("David", 25);
|
||||
f = p.knows(p2);
|
||||
tx.success();
|
||||
}
|
||||
finally
|
||||
{
|
||||
tx.finish();
|
||||
}
|
||||
Transaction tx2 = graphDatabaseService.beginTx();
|
||||
try
|
||||
{
|
||||
f.remove();
|
||||
tx2.success();
|
||||
}
|
||||
finally
|
||||
{
|
||||
tx2.finish();
|
||||
}
|
||||
assertFalse("Unexpected relationship entity found.", friendshipRepository.findAll().iterator().hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveRelationshipEntityIfNodeEntityIsRemoved() {
|
||||
cleanDb();
|
||||
Person p;
|
||||
Transaction tx = graphDatabaseService.beginTx();
|
||||
try
|
||||
{
|
||||
p = persistedPerson("Michael", 35);
|
||||
Person p2 = persistedPerson("David", 25);
|
||||
p.knows(p2);
|
||||
tx.success();
|
||||
}
|
||||
finally
|
||||
{
|
||||
tx.finish();
|
||||
}
|
||||
Transaction tx2 = graphDatabaseService.beginTx();
|
||||
try
|
||||
{
|
||||
p.remove();
|
||||
tx2.success();
|
||||
}
|
||||
finally
|
||||
{
|
||||
tx2.finish();
|
||||
}
|
||||
assertFalse("Unexpected relationship entity found.", friendshipRepository.findAll().iterator().hasNext());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,11 +113,11 @@ public class SubReferenceNodeTypeRepresentationStrategyTest {
|
||||
@Transactional
|
||||
public void testPreEntityRemoval() throws Exception {
|
||||
Node typeNode = getInstanceofRelationship(thingNode).getOtherNode(thingNode);
|
||||
nodeTypeRepresentationStrategy.preEntityRemoval(thing);
|
||||
nodeTypeRepresentationStrategy.preEntityRemoval(node(thing));
|
||||
assertNull("instanceof relationship was removed", getInstanceofRelationship(thingNode));
|
||||
assertNotNull("instanceof relationship was removed", getInstanceofRelationship(subThingNode));
|
||||
assertEquals("no things left after removal", 1, typeNode.getProperty(SubReferenceNodeTypeRepresentationStrategy.SUBREFERENCE_NODE_COUNTER_KEY));
|
||||
nodeTypeRepresentationStrategy.preEntityRemoval(subThing);
|
||||
nodeTypeRepresentationStrategy.preEntityRemoval(node(subThing));
|
||||
assertNull("instanceof relationship was removed", getInstanceofRelationship(subThingNode));
|
||||
assertEquals("no things left after removal", 0, typeNode.getProperty(SubReferenceNodeTypeRepresentationStrategy.SUBREFERENCE_NODE_COUNTER_KEY));
|
||||
|
||||
|
||||
@@ -19,8 +19,7 @@ import java.util.Iterator;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.graph.neo4j.template.PropertyMap._;
|
||||
import static org.springframework.data.graph.neo4j.template.PropertyMap.props;
|
||||
import static org.springframework.data.graph.core.Property._;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
@@ -189,7 +188,7 @@ public class Neo4jTemplateApiTest {
|
||||
|
||||
@Test
|
||||
public void testCreateNodeWithProperties() throws Exception {
|
||||
Node node=template.createNode(props().set("test", "testCreateNodeWithProperties").toMap());
|
||||
Node node=template.createNode(_("test", "testCreateNodeWithProperties"));
|
||||
assertTestPropertySet(node, "testCreateNodeWithProperties");
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ import java.util.Set;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.neo4j.kernel.Traversal.returnAllButStartNode;
|
||||
import static org.springframework.data.graph.core.Property._;
|
||||
import static org.springframework.data.graph.neo4j.template.NeoTraversalTest.Type.HAS;
|
||||
import static org.springframework.data.graph.neo4j.template.PropertyMap._;
|
||||
|
||||
public class NeoTraversalTest extends NeoApiTest {
|
||||
|
||||
|
||||
@@ -14,5 +14,11 @@
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
|
||||
|
||||
<!--<bean id="typeRepresentationStrategy" class="org.springframework.data.graph.neo4j.support.NoopTypeRepresentationStrategy" />-->
|
||||
<context:annotation-config />
|
||||
|
||||
|
||||
<bean name="graphDatabase" class="org.springframework.data.graph.core.GraphDatabaseFactory">
|
||||
<property name="storeLocation" value="target/test-db"/>
|
||||
|
||||
</bean>
|
||||
</beans>
|
||||
@@ -152,6 +152,10 @@
|
||||
<property name="repositoryInterface" value="org.springframework.data.graph.neo4j.GroupRepository" />
|
||||
<property name="graphDatabaseContext" ref="graphDatabaseContext"/>
|
||||
</bean>
|
||||
<bean id="friendshipRepository" class="org.springframework.data.graph.neo4j.repository.GraphRepositoryFactoryBean">
|
||||
<property name="repositoryInterface" value="org.springframework.data.graph.neo4j.FriendshipRepository" />
|
||||
<property name="graphDatabaseContext" ref="graphDatabaseContext"/>
|
||||
</bean>
|
||||
|
||||
<!-- Adds transaparent exception translation to the DAOs -->
|
||||
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:jee="http://www.springframework.org/schema/jee"
|
||||
xmlns:tx="http://www.springframework.org/schema/tx"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:task="http://www.springframework.org/schema/task"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
|
||||
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
|
||||
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
|
||||
|
||||
<bean id="noopNodeStrategy" class="org.springframework.data.graph.neo4j.support.NoopNodeTypeRepresentationStrategy" />
|
||||
<bean id="noopRelationshipStrategy" class="org.springframework.data.graph.neo4j.support.NoopRelationshipTypeRepresentationStrategy" />
|
||||
</beans>
|
||||
@@ -23,10 +23,11 @@
|
||||
backing graph store afterwards.
|
||||
</para>
|
||||
<para>
|
||||
The connection between the two entities is kept via a FOREIGN_ID field in the node that contains the JPA id (currently only single
|
||||
value ids are supported). The entity class can be resolved via the NodeTypeStrategy that preserves the Java type hierarchy within the graph.
|
||||
With the id and class, you can then retrieve the appropriate JPA entity for a given node.
|
||||
</para>
|
||||
The connection between the two entities is kept via a FOREIGN_ID field in the node that contains the JPA id
|
||||
(currently only single value ids are supported). The entity class can be resolved via the
|
||||
TypeRepresentationStrategy that manages the Java type hierarchy within the graph. With the id and class,
|
||||
you can then retrieve the appropriate JPA entity for a given node.
|
||||
</para>
|
||||
<para>
|
||||
The other direction is handled by indexing the Node with the FOREIGN_ID index which contains a concatenation of the fully qualified class
|
||||
name of the JPA entity and the id. So it is possible on instantiation of a JPA id via the entity manager (or some other means like creating
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
||||
<section>
|
||||
<title>Finding nodes with finders</title>
|
||||
<note>TODO: rewrite to repositories</note>
|
||||
<para>Spring Data Graph also comes with a typed, repository-like Finder implementation that provides methods for
|
||||
locating nodes and relationships. Those methods return instances of the node and relationship entities,
|
||||
not the graph primitives from Neo4j. Finders delegate to the configured <code>NodeTypeStrategy</code> for type
|
||||
@@ -68,15 +69,8 @@ Iterable<Person> davesFriends = graphRepository.findAllByTraversal(dave,
|
||||
Internally the mapping from java types to the graph is handled by a <code>NodeTypeStrategy</code> instance
|
||||
that is configured with the <code>GraphDatabaseContext</code>. The strategy is called on entity creation and
|
||||
removal and provides methods for retrieving entities based on type. It also comes with methods confirming or
|
||||
retrieving java types from the actual graph node. (see also <xref linkend="nodetypestrategy"/>)
|
||||
</para>
|
||||
<para>
|
||||
The default strategy (<code>IndexingNodeTypeStrategy</code>)
|
||||
uses indexing (index "__types__") and node properties ("__type__") to store the type information in the graph.
|
||||
|
||||
The second strategy uses an in graph structure to represent the inheritance hierarchy links the actual node
|
||||
entity nodes to their concrete class nodes. Instance counts are updated on each of the class nodes in the hierarchy.
|
||||
The last provided strategy is a No-Op strategy that doesn't care about type information.
|
||||
retrieving java types from the actual graph node. (see also
|
||||
<xref linkend="reference:programming-model:typerepresentationstrategy"/>)
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
@@ -1,48 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
||||
<section id="nodetypestrategy">
|
||||
<title>Storing Type Information in the Graph</title>
|
||||
<para>
|
||||
There are several ways to represent the Java type hierarchy of the data model in the graph. In general for all
|
||||
node and relationship entities type information is needed to perform certain repository operations. Some of
|
||||
this type information is saved in the graph database.
|
||||
</para>
|
||||
<para>
|
||||
Implementations of <code>NodeTypeStrategy</code> take care of persisting this information on entity instance
|
||||
creation. They also provide the repository methods that use this type information to perform their operations
|
||||
like findAll, count, etc.
|
||||
</para>
|
||||
<para>
|
||||
There are three available implementations to choose from.
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para><code>IndexingNodeTypeStrategy</code></para>
|
||||
<para>
|
||||
Stores entity types in the integrated index. Each entity node gets indexed with its type and
|
||||
any supertypes that are also <code>@NodeEntity</code>-annotated. The special index used for this
|
||||
is called <code>__types__</code>. Additionally, in order to get the type of an entity node, each
|
||||
node has a property <code>__type__</code> with the type of that entity.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><code>SubReferenceNodeTypeStrategy</code></para>
|
||||
<para>
|
||||
Stores entity types in a tree in the graph representing the type hierarchy. Each entity
|
||||
has a INSTANCE_OF relationship to a type node representing that entity's type. The type may or
|
||||
may not have a SUBCLASS_OF relationship to another type node.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><code>NoopNodeTypeStrategy</code></para>
|
||||
<para>
|
||||
Does not store any type information, and does hence not support finding by type, counting by type,
|
||||
or retrieving the type of any entity.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
The default implementation is <code>IndexingNodeTypeStrategy</code> for new graphs. If using an existing
|
||||
graph, Spring Data Graph will default to the strategy first used when the graph was created.
|
||||
</para>
|
||||
</section>
|
||||
@@ -14,7 +14,7 @@
|
||||
<xi:include href="finders.xml"/>
|
||||
<xi:include href="transactions.xml"/>
|
||||
<xi:include href="attachdetach.xml"/>
|
||||
<xi:include href="nodetypestrategy.xml"/>
|
||||
<xi:include href="typerepresentationstrategy.xml"/>
|
||||
<xi:include href="introducedmethods.xml"/>
|
||||
<xi:include href="projection.xml"/>
|
||||
<xi:include href="beanvalidation.xml"/>
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
||||
<section id="reference:programming-model:typerepresentationstrategy">
|
||||
<title>Storing type information in the graph</title>
|
||||
<para>
|
||||
There are several ways to represent the Java type hierarchy of the data model in the graph. In general, for all
|
||||
node and relationship entities, type information is needed to perform certain repository operations. Some of
|
||||
this type information is saved in the graph database.
|
||||
</para>
|
||||
<para>
|
||||
Implementations of
|
||||
<code>TypeRepresentationStrategy</code>
|
||||
take care of persisting this information on entity instance
|
||||
creation. They also provide the repository methods that use this type information to perform their operations,
|
||||
like findAll and count.
|
||||
</para>
|
||||
<para>
|
||||
There are three available implementations for node entities to choose from.
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
<code>IndexingNodeTypeRepresentationStrategy</code>
|
||||
</para>
|
||||
<para>
|
||||
Stores entity types in the integrated index. Each entity node gets indexed with its type and
|
||||
any supertypes that are also<code>@NodeEntity</code>-annotated. The special index used for this
|
||||
is called<code>__types__</code>. Additionally, in order to get the type of an entity node, each
|
||||
node has a property
|
||||
<code>__type__</code>
|
||||
with the type of that entity.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<code>SubReferenceNodeTypeRepresentationStrategy</code>
|
||||
</para>
|
||||
<para>
|
||||
Stores entity types in a tree in the graph representing the type hierarchy. Each entity
|
||||
has a INSTANCE_OF relationship to a type node representing that entity's type. The type may or
|
||||
may not have a SUBCLASS_OF relationship to another type node.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<code>NoopNodeTypeRepresentationStrategy</code>
|
||||
</para>
|
||||
<para>
|
||||
Does not store any type information, and does hence not support finding by type, counting by type,
|
||||
or retrieving the type of any entity.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
There are two implementations for relationship entities available, same behavior as the corresponding ones
|
||||
above:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
<code>IndexingRelationshipTypeRepresentationStrategy</code>
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<code>NoopRelationshipTypeRepresentationStrategy</code>
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
Spring Data Graph will by default autodetect which are the most suitable strategies for node and relationship
|
||||
entities. For new data stores, it will always opt for the indexing strategies. If a data store was created
|
||||
with the older<code>SubReferenceNodeTypeRepresentationStrategy</code>, then it will continue to use that
|
||||
strategy for node entities. It will however in that case use the no-op strategy for relationship entities,
|
||||
which means that the old data stores have no support for searching for relationship entities. The indexing
|
||||
strategies are recommended for all new users.
|
||||
</para>
|
||||
</section>
|
||||
@@ -98,6 +98,7 @@
|
||||
</section>
|
||||
<section>
|
||||
<title>Setting Up Spring Data Graph - Spring Configuration</title>
|
||||
<note>Out of date - should we even have this section?</note>
|
||||
<para>The concrete configuration for Spring Data Graph is quite verbose as there is no autowiring involved. It sets up the following parts.
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
@@ -120,7 +121,7 @@
|
||||
<para>Finder factory</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>an appropriate NodeTypeStrategy</para>
|
||||
<para>TypeRepresentationStrategies</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user