diff --git a/spring-data-graph-parent/pom.xml b/spring-data-graph-parent/pom.xml index 246ef3e3d..01749384a 100644 --- a/spring-data-graph-parent/pom.xml +++ b/spring-data-graph-parent/pom.xml @@ -16,7 +16,7 @@ 1.5.10 3.0.5.RELEASE 1.1.0.BUILD-SNAPSHOT - 1.4.M04 + 1.4-SNAPSHOT 1.6.12.M1 diff --git a/spring-data-neo4j-rest/pom.xml b/spring-data-neo4j-rest/pom.xml index 80d362c65..5f027d10f 100644 --- a/spring-data-neo4j-rest/pom.xml +++ b/spring-data-neo4j-rest/pom.xml @@ -55,7 +55,7 @@ neo4j - org.neo4j + org.neo4j.server.plugin neo4j-cypher-plugin ${neo4j.version} diff --git a/spring-data-neo4j-rest/src/main/java/org/springframework/data/graph/neo4j/rest/support/RestGraphDatabase.java b/spring-data-neo4j-rest/src/main/java/org/springframework/data/graph/neo4j/rest/support/RestGraphDatabase.java index 4a31442f1..e46a071ad 100644 --- a/spring-data-neo4j-rest/src/main/java/org/springframework/data/graph/neo4j/rest/support/RestGraphDatabase.java +++ b/spring-data-neo4j-rest/src/main/java/org/springframework/data/graph/neo4j/rest/support/RestGraphDatabase.java @@ -22,10 +22,13 @@ import org.neo4j.graphdb.event.KernelEventHandler; import org.neo4j.graphdb.event.TransactionEventHandler; import org.neo4j.graphdb.index.Index; import org.neo4j.graphdb.traversal.TraversalDescription; +import org.springframework.core.convert.ConversionService; import org.springframework.data.graph.core.GraphDatabase; import org.springframework.data.graph.core.Property; import org.springframework.data.graph.neo4j.rest.support.index.RestIndexManager; +import org.springframework.data.graph.neo4j.support.query.ConversionServiceQueryResultConverter; import org.springframework.data.graph.neo4j.support.query.QueryEngine; +import org.springframework.data.graph.neo4j.support.query.QueryResultConverter; import javax.ws.rs.core.Response.Status; import java.net.URI; @@ -35,6 +38,7 @@ public class RestGraphDatabase implements GraphDatabaseService, GraphDatabase { private RestRequest restRequest; private long propertyRefetchTimeInMillis = 1000; + private ConversionService conversionService; public RestGraphDatabase( URI uri ) { @@ -99,7 +103,18 @@ public class RestGraphDatabase implements GraphDatabaseService, GraphDatabase { @Override public QueryEngine queryEngineFor(QueryEngine.Type type) { - return new RestQueryEngine(restRequest); + return new RestQueryEngine(this, createResultConverter()); + } + + private ConversionServiceQueryResultConverter createResultConverter() { + if (conversionService==null) return null; + return new ConversionServiceQueryResultConverter(conversionService); + } + + @Override + public void setConversionService(ConversionService conversionService) { + + this.conversionService = conversionService; } public RestIndexManager index() { diff --git a/spring-data-neo4j-rest/src/main/java/org/springframework/data/graph/neo4j/rest/support/RestQueryEngine.java b/spring-data-neo4j-rest/src/main/java/org/springframework/data/graph/neo4j/rest/support/RestQueryEngine.java new file mode 100644 index 000000000..e76120603 --- /dev/null +++ b/spring-data-neo4j-rest/src/main/java/org/springframework/data/graph/neo4j/rest/support/RestQueryEngine.java @@ -0,0 +1,141 @@ +/** + * Copyright 2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.graph.neo4j.rest.support; + +import com.sun.jersey.api.client.ClientResponse; +import org.neo4j.helpers.collection.IterableWrapper; +import org.springframework.dao.InvalidDataAccessResourceUsageException; +import org.springframework.data.graph.neo4j.support.query.QueryEngine; +import org.springframework.data.graph.neo4j.support.query.QueryResultConverter; + +import java.util.*; + +/** + * @author mh + * @since 22.06.11 + */ +public class RestQueryEngine implements QueryEngine { + private final RestRequest restRequest; + private final RestGraphDatabase restGraphDatabase; + private final QueryResultConverter resultConverter; + + public RestQueryEngine(RestGraphDatabase restGraphDatabase, QueryResultConverter resultConverter) { + this.restGraphDatabase = restGraphDatabase; + this.resultConverter = resultConverter; + this.restRequest = restGraphDatabase.getRestRequest(); + } + + @Override + public Iterable> query(String statement) { + return executeStatement(statement).getData(); + } + + private RestQueryResult executeStatement(String statement) { + final ClientResponse response = restRequest.get("ext/CypherPlugin/graphdb/execute_query", JsonHelper.createJsonFrom(Collections.singletonMap("query", statement))); + return new RestQueryResult(restRequest.toMap(response)); + } + + class RestQueryResult { + List columns; + List> data; + + public RestQueryResult(Map result) { + columns= (List) result.get("columns"); + extractData(result); + } + + private void extractData(Map result) { + List> rows= (List>) result.get("data"); + data=new ArrayList>(rows.size()); + for (List row : rows) { + data.add(mapRow(row)); + } + } + + private Map mapRow(List row) { + int columnCount=columns.size(); + Map newRow=new HashMap(columnCount); + for (int i = 0; i < columnCount; i++) { + final Object value = row.get(i); + newRow.put(columns.get(i), convertValue(value)); + } + return newRow; + } + + private Object convertValue(Object value) { + final Object representationValue = convertFromRepresentation(value); + return resultConverter.convertValue(representationValue, null); + } + + private Object convertFromRepresentation(Object value) { + if (value instanceof Map) { + RestEntity restEntity = createRestEntity((Map) value); + if (restEntity!=null) return restEntity; + } + return value; + } + + private RestEntity createRestEntity(Map data) { + final String uri = (String) data.get("self"); + if (uri == null || uri.isEmpty()) return null; + if (uri.contains("/node/")) { + return new RestNode(data,restGraphDatabase); + } + if (uri.contains("/relationship/")) { + return new RestRelationship(data,restGraphDatabase); + } + return null; + } + + public List> getData() { + return data; + } + + public List getColumns() { + return columns; + } + + public T getSingleValue(Class type) { + if (data.size()==0) throw new InvalidDataAccessResourceUsageException("Expected single result, got none"); + if (data.size()!=1) throw new InvalidDataAccessResourceUsageException("Expected single result, got more than one"); + return getSingleColumn(type).iterator().next(); + } + + public Iterable getSingleColumn(final Class type) { + if (columns.size()==0) throw new InvalidDataAccessResourceUsageException("Expected single column, got none"); + if (columns.size()!=1) throw new InvalidDataAccessResourceUsageException("Expected single column, got more than one"); + final String firstColumn = columns.get(0); + return new IterableWrapper>(data) { + @Override + protected T underlyingObjectToObject(Map row) { + return resultConverter.convertValue(row.get(firstColumn),type); + } + }; + } + } + + @Override + public Iterable query(String statement, Class type) { + final RestQueryResult restQueryResult = executeStatement(statement); + return restQueryResult.getSingleColumn(type); + } + + @Override + public T queryForObject(String statement, Class type) { + return executeStatement(statement).getSingleValue(type); + } +} diff --git a/spring-data-neo4j-rest/src/main/java/org/springframework/data/graph/neo4j/rest/support/RestRequest.java b/spring-data-neo4j-rest/src/main/java/org/springframework/data/graph/neo4j/rest/support/RestRequest.java index d60a70676..644ef64c1 100644 --- a/spring-data-neo4j-rest/src/main/java/org/springframework/data/graph/neo4j/rest/support/RestRequest.java +++ b/spring-data-neo4j-rest/src/main/java/org/springframework/data/graph/neo4j/rest/support/RestRequest.java @@ -79,6 +79,14 @@ public class RestRequest { return builder( path ).get( ClientResponse.class ); } + public ClientResponse get( String path, String data ) { + Builder builder = builder(path); + if ( data != null ) { + builder = builder.entity( data, MediaType.APPLICATION_JSON_TYPE ); + } + return builder.get(ClientResponse.class); + } + public ClientResponse delete( String path ) { return builder( path ).delete( ClientResponse.class ); } diff --git a/spring-data-neo4j-rest/src/test/java/org/springframework/data/graph/neo4j/rest/integration/RestNeo4jTemplateTest.java b/spring-data-neo4j-rest/src/test/java/org/springframework/data/graph/neo4j/rest/integration/RestNeo4jTemplateTest.java index ad0fb8a27..c6fcba7db 100644 --- a/spring-data-neo4j-rest/src/test/java/org/springframework/data/graph/neo4j/rest/integration/RestNeo4jTemplateTest.java +++ b/spring-data-neo4j-rest/src/test/java/org/springframework/data/graph/neo4j/rest/integration/RestNeo4jTemplateTest.java @@ -50,7 +50,9 @@ public class RestNeo4jTemplateTest extends Neo4jTemplateTest protected GraphDatabase createGraphDatabase() throws Exception { testHelper.cleanDb(); - return testHelper.createGraphDatabase(); + final GraphDatabase graphDatabase = testHelper.createGraphDatabase(); + graphDatabase.setConversionService(conversionService); + return graphDatabase; } @Override diff --git a/spring-data-neo4j-rest/src/test/java/org/springframework/data/graph/neo4j/rest/support/RestQueryEngineTest.java b/spring-data-neo4j-rest/src/test/java/org/springframework/data/graph/neo4j/rest/support/RestQueryEngineTest.java new file mode 100644 index 000000000..59bbd2d86 --- /dev/null +++ b/spring-data-neo4j-rest/src/test/java/org/springframework/data/graph/neo4j/rest/support/RestQueryEngineTest.java @@ -0,0 +1,53 @@ +package org.springframework.data.graph.neo4j.rest.support; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.graph.core.GraphDatabase; +import org.springframework.data.graph.neo4j.support.DelegatingGraphDatabase; +import org.springframework.data.graph.neo4j.support.GraphRepositoryTest; +import org.springframework.data.graph.neo4j.support.query.QueryEngineTest; +import org.springframework.test.context.CleanContextCacheTestExecutionListener; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.transaction.BeforeTransaction; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; + +/** + * @author mh + * @since 23.06.11 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@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 RestQueryEngineTest extends QueryEngineTest { + + @Autowired + RestGraphDatabase restGraphDatabase; + + @BeforeClass + public static void startDb() throws Exception { + RestTestBase.startDb(); + } + + @BeforeTransaction + public void cleanDb() { + RestTestBase.cleanDb(); + } + + @AfterClass + public static void shutdownDb() { + RestTestBase.shutdownDb(); + } + + @Override + protected GraphDatabase createGraphDatabase() throws Exception { + restGraphDatabase.setConversionService(conversionService); + return restGraphDatabase; + } +} \ No newline at end of file diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabase.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabase.java index e933d8587..c96facad3 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabase.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabase.java @@ -22,6 +22,9 @@ import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.RelationshipType; import org.neo4j.graphdb.index.Index; import org.neo4j.graphdb.traversal.TraversalDescription; +import org.springframework.core.convert.ConversionService; +import org.springframework.data.graph.neo4j.support.query.EmbeddedQueryEngine; +import org.springframework.data.graph.neo4j.support.query.QueryEngine; public interface GraphDatabase { @@ -91,4 +94,8 @@ public interface GraphDatabase { * @return a TraversalDescription as starting point for defining a traversal */ TraversalDescription createTraversalDescription(); + + QueryEngine queryEngineFor(EmbeddedQueryEngine.Type type); + + void setConversionService(ConversionService conversionService); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/DelegatingGraphDatabase.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/DelegatingGraphDatabase.java index 738f10f4b..c734f6fd4 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/DelegatingGraphDatabase.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/DelegatingGraphDatabase.java @@ -22,8 +22,13 @@ import org.neo4j.graphdb.index.IndexManager; import org.neo4j.graphdb.traversal.TraversalDescription; import org.neo4j.index.impl.lucene.LuceneIndexImplementation; import org.neo4j.kernel.Traversal; +import org.springframework.core.convert.ConversionService; import org.springframework.data.graph.core.GraphDatabase; import org.springframework.data.graph.core.Property; +import org.springframework.data.graph.neo4j.support.query.ConversionServiceQueryResultConverter; +import org.springframework.data.graph.neo4j.support.query.EmbeddedQueryEngine; +import org.springframework.data.graph.neo4j.support.query.QueryEngine; +import org.springframework.data.graph.neo4j.support.query.QueryResultConverter; import java.util.Map; @@ -34,14 +39,14 @@ import java.util.Map; public class DelegatingGraphDatabase implements GraphDatabase { protected GraphDatabaseService delegate; + private ConversionService conversionService; public DelegatingGraphDatabase(final GraphDatabaseService delegate) { this.delegate = delegate; } - @Override - public Node getReferenceNode() { - return delegate.getReferenceNode(); + public void setConversionService(ConversionService conversionService) { + this.conversionService = conversionService; } @Override @@ -122,7 +127,22 @@ public class DelegatingGraphDatabase implements GraphDatabase { return Traversal.description(); } + @Override + public QueryEngine queryEngineFor(EmbeddedQueryEngine.Type type) { + return new EmbeddedQueryEngine(delegate, createResultConverter()); + } + + private ConversionServiceQueryResultConverter createResultConverter() { + if (conversionService == null) return null; + return new ConversionServiceQueryResultConverter(conversionService); + } + public void shutdown() { delegate.shutdown(); } + + @Override + public Node getReferenceNode() { + return delegate.getReferenceNode(); + } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/query/ConversionServiceQueryResultConverter.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/query/ConversionServiceQueryResultConverter.java new file mode 100644 index 000000000..6cec3791c --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/query/ConversionServiceQueryResultConverter.java @@ -0,0 +1,37 @@ +/** + * Copyright 2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.graph.neo4j.support.query; + +import org.springframework.core.convert.ConversionService; + +/** + * @author mh + * @since 22.06.11 + */ +public class ConversionServiceQueryResultConverter implements QueryResultConverter { + private final ConversionService conversionService; + + public ConversionServiceQueryResultConverter(ConversionService conversionService) { + this.conversionService = conversionService; + } + + @Override + public T convertValue(Object value, Class type) { + if (type==null || type.isInstance(value)) return (T) value; + return conversionService.convert(value,type); + } +} diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/query/EmbeddedQueryEngine.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/query/EmbeddedQueryEngine.java new file mode 100644 index 000000000..e50bb4b4b --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/query/EmbeddedQueryEngine.java @@ -0,0 +1,126 @@ +/** + * Copyright 2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.graph.neo4j.support.query; + +import org.neo4j.cypher.SyntaxError; +import org.neo4j.cypher.commands.Query; +import org.neo4j.cypher.javacompat.CypherParser; +import org.neo4j.cypher.javacompat.ExecutionEngine; +import org.neo4j.cypher.javacompat.ExecutionResult; +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.helpers.collection.IterableWrapper; +import org.springframework.dao.InvalidDataAccessResourceUsageException; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public class EmbeddedQueryEngine implements QueryEngine { + + public EmbeddedQueryEngine(GraphDatabaseService graphDatabaseService) { + this(graphDatabaseService, QueryResultConverter.NO_OP_QUERY_RESULT_CONVERTER); + } + + final ExecutionEngine executionEngine; + private QueryResultConverter resultConverter; + + public EmbeddedQueryEngine(GraphDatabaseService graphDatabaseService, QueryResultConverter resultConverter) { + this.resultConverter = resultConverter != null ? resultConverter : QueryResultConverter.NO_OP_QUERY_RESULT_CONVERTER; + this.executionEngine = new ExecutionEngine(graphDatabaseService); + } + + @Override + public Iterable> query(String statement) { + try { + ExecutionResult result = parseAndExecuteQuery(statement); + return convertResult(result); + } catch (Exception e) { + throw new InvalidDataAccessResourceUsageException("Error executing statement " + statement, e); + } + } + + @Override + public Iterable query(String statement, Class type) { + try { + ExecutionResult result = parseAndExecuteQuery(statement); + return convertResult(result, type); + } catch (Exception e) { + throw new InvalidDataAccessResourceUsageException("Error executing statement " + statement + " for type " + type, e); + } + } + + @Override + public T queryForObject(String statement, Class type) { + try { + ExecutionResult result = parseAndExecuteQuery(statement); + final Iterable convertedResult = convertResult(result, type); + return extractSingleResult(convertedResult); + } catch (Exception e) { + throw new InvalidDataAccessResourceUsageException("Error executing statement " + statement + " for type " + type, e); + } + } + + private ExecutionResult parseAndExecuteQuery(String statement) { + try { + CypherParser parser = new CypherParser(); + Query query = parser.parse(statement); + return executionEngine.execute(query); + } catch (SyntaxError syntaxError) { + throw new InvalidDataAccessResourceUsageException("Error executing statement " + statement, syntaxError); + } + } + + private T extractSingleResult(Iterable convertedResult) { + final Iterator it = convertedResult.iterator(); + if (!it.hasNext()) throw new InvalidDataAccessResourceUsageException("Expected single result, got none"); + T value = it.hasNext() ? it.next() : null; + if (it.hasNext()) + throw new InvalidDataAccessResourceUsageException("Expected single result, got more than one"); + return value; + } + + private Iterable convertResult(ExecutionResult result, final Class type) { + final List columns = result.columns(); + if (columns.size() != 1) + throw new InvalidDataAccessResourceUsageException("Expected single column of results, got " + columns); + final String column = columns.get(0); + return new IterableWrapper>(result) { + @Override + protected T underlyingObjectToObject(Map row) { + return resultConverter.convertValue(row.get(column), type); + } + }; + } + + private Iterable> convertResult(Iterable> result) { + return new IterableWrapper, Map>(result) { + @Override + protected Map underlyingObjectToObject(Map row) { + Map newRow=new HashMap(row); // todo performance + for (Map.Entry entry : newRow.entrySet()) { + Object value = resultConverter.convertValue(entry.getValue(),null); + if (value != entry.getValue()) { + entry.setValue(value); + } + } + return row; + } + }; + } + +} \ No newline at end of file diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/query/QueryEngine.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/query/QueryEngine.java new file mode 100644 index 000000000..b3e5904d1 --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/query/QueryEngine.java @@ -0,0 +1,33 @@ +/** + * Copyright 2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.graph.neo4j.support.query; + +import java.util.Map; + +/** + * @author mh + * @since 22.06.11 + */ +public interface QueryEngine { + Iterable> query(String statement); + + Iterable query(String statement, Class type); + + T queryForObject(String statement, Class type); + + public enum Type { Cypher, Gremlin } +} diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/query/QueryExecutor.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/query/QueryExecutor.java index b62d77581..393b21726 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/query/QueryExecutor.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/query/QueryExecutor.java @@ -16,22 +16,12 @@ package org.springframework.data.graph.neo4j.support.query; -import org.neo4j.cypher.SyntaxError; -import org.neo4j.cypher.commands.Query; -import org.neo4j.cypher.javacompat.CypherParser; -import org.neo4j.cypher.javacompat.ExecutionEngine; -import org.neo4j.cypher.javacompat.ExecutionResult; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Relationship; -import org.neo4j.helpers.collection.IterableWrapper; import org.springframework.core.convert.ConversionService; -import org.springframework.dao.InvalidDataAccessResourceUsageException; import org.springframework.data.graph.core.TypeRepresentationStrategy; import org.springframework.data.graph.neo4j.support.GraphDatabaseContext; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; import java.util.Map; /** @@ -39,83 +29,29 @@ import java.util.Map; * @since 10.06.11 * todo limits */ -public class QueryExecutor { +public class QueryExecutor implements QueryResultConverter { private final TypeRepresentationStrategy nodeTypeRepresentationStrategy; - private final ExecutionEngine executionEngine; private final TypeRepresentationStrategy relationshipTypeRepresentationStrategy; private final ConversionService conversionService; + private final QueryEngine queryEngine; public QueryExecutor(GraphDatabaseContext ctx) { this.nodeTypeRepresentationStrategy = ctx.getNodeTypeRepresentationStrategy(); relationshipTypeRepresentationStrategy = ctx.getRelationshipTypeRepresentationStrategy(); conversionService = ctx.getConversionService(); - this.executionEngine = new ExecutionEngine(ctx.getGraphDatabaseService()); + queryEngine = new EmbeddedQueryEngine(ctx.getGraphDatabaseService(), this); } public Iterable> query(String statement) { - try { - ExecutionResult result = parseAndExecuteQuery(statement); - return convertResult(result); - } catch (Exception e) { - throw new InvalidDataAccessResourceUsageException("Error executing statement " + statement, e); - } + return queryEngine.query(statement); } public Iterable query(String statement, Class type) { - try { - ExecutionResult result = parseAndExecuteQuery(statement); - return convertResult(result, type); - } catch (Exception e) { - throw new InvalidDataAccessResourceUsageException("Error executing statement " + statement + " for type " + type, e); - } + return queryEngine.query(statement, type); } public T queryForObject(String statement, Class type) { - try { - ExecutionResult result = parseAndExecuteQuery(statement); - final Iterable convertedResult = convertResult(result, type); - return extractSingleResult(convertedResult); - } catch (Exception e) { - throw new InvalidDataAccessResourceUsageException("Error executing statement " + statement + " for type " + type, e); - } - } - - private T extractSingleResult(Iterable convertedResult) { - final Iterator it = convertedResult.iterator(); - if (!it.hasNext()) throw new InvalidDataAccessResourceUsageException("Expected single result, got none"); - T value = it.hasNext() ? it.next() : null; - if (it.hasNext()) - throw new InvalidDataAccessResourceUsageException("Expected single result, got more than one"); - return value; - } - - private Iterable convertResult(ExecutionResult result, final Class type) { - final List columns = result.columns(); - if (columns.size() != 1) - throw new InvalidDataAccessResourceUsageException("Expected single column of results, got " + columns); - final String column = columns.get(0); - return new IterableWrapper>(result) { - @Override - protected T underlyingObjectToObject(Map row) { - return convertValue(row.get(column), type); - } - }; - } - - private Iterable> convertResult(Iterable> result) { - return new IterableWrapper, Map>(result) { - @Override - protected Map underlyingObjectToObject(Map row) { - Map newRow=new HashMap(row); // todo performance - for (Map.Entry entry : newRow.entrySet()) { - Object value = convertValue(entry.getValue()); - if (value != entry.getValue()) { - entry.setValue(value); - } - } - return row; - } - }; + return queryEngine.queryForObject(statement,type); } private Object convertValue(Object value) { @@ -127,23 +63,16 @@ public class QueryExecutor { } return value; } - private T convertValue(Object value,Class type) { + + public T convertValue(Object value, Class type) { + if (type == null) return (T) convertValue(value); + if (type.isInstance(value)) return type.cast(value); if (value instanceof Node) { - return (T) nodeTypeRepresentationStrategy.createEntity((Node) value,type); + return (T) nodeTypeRepresentationStrategy.createEntity((Node) value, type); } if (value instanceof Relationship) { - return (T) relationshipTypeRepresentationStrategy.createEntity((Relationship) value,type); - } - return conversionService.convert(value,type); - } - - private ExecutionResult parseAndExecuteQuery(String statement) { - try { - CypherParser parser = new CypherParser(); - Query query = parser.parse(statement); - return executionEngine.execute(query); - } catch (SyntaxError syntaxError) { - throw new InvalidDataAccessResourceUsageException("Error executing statement " + statement, syntaxError); + return (T) relationshipTypeRepresentationStrategy.createEntity((Relationship) value, type); } + return conversionService.convert(value, type); } } \ No newline at end of file diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/query/QueryResultConverter.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/query/QueryResultConverter.java new file mode 100644 index 000000000..020205405 --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/query/QueryResultConverter.java @@ -0,0 +1,32 @@ +/** + * Copyright 2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.graph.neo4j.support.query; + +/** + * @author mh + * @since 22.06.11 + */ +public interface QueryResultConverter { + T convertValue(Object value, Class type); + + QueryResultConverter NO_OP_QUERY_RESULT_CONVERTER = new QueryResultConverter() { + @Override + public T convertValue(Object value, Class type) { + return (T) value; + } + }; +} diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/template/Neo4jOperations.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/template/Neo4jOperations.java index a4d857115..fb04184f2 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/template/Neo4jOperations.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/template/Neo4jOperations.java @@ -21,6 +21,10 @@ import org.neo4j.graphdb.traversal.TraversalDescription; import org.neo4j.helpers.collection.ClosableIterable; import org.springframework.data.graph.neo4j.support.path.PathMapper; import org.springframework.data.graph.core.Property; +import org.springframework.data.graph.neo4j.support.query.EmbeddedQueryEngine; +import org.springframework.data.graph.neo4j.support.query.QueryEngine; + +import java.util.Map; /** * A template with convenience operations, exception translation and implicit transaction for modifying methods @@ -163,4 +167,11 @@ public interface Neo4jOperations { */ T index(String indexName, T element, String field, Object value); + + Iterable> query(QueryEngine.Type engineType, String statement); + + Iterable query(QueryEngine.Type engineType, String statement, Class type); + + T queryForObject(QueryEngine.Type engineType, String statement, Class type); + } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/template/Neo4jTemplate.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/template/Neo4jTemplate.java index aa727bb1f..2bc23b8a1 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/template/Neo4jTemplate.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/template/Neo4jTemplate.java @@ -31,6 +31,8 @@ import org.springframework.data.graph.neo4j.support.path.NodePath; import org.springframework.data.graph.neo4j.support.path.PathMapper; import org.springframework.data.graph.neo4j.support.path.PathMappingIterator; import org.springframework.data.graph.neo4j.support.path.RelationshipPath; +import org.springframework.data.graph.neo4j.support.query.EmbeddedQueryEngine; +import org.springframework.data.graph.neo4j.support.query.QueryEngine; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; @@ -179,7 +181,7 @@ public class Neo4jTemplate implements Neo4jOperations { @Override public ClosableIterable query(String indexName, final PathMapper pathMapper, String field, String value) { - notNull(field, "field", value, "value", pathMapper, "pathMapper",indexName,"indexName"); + notNull(field, "field", value, "value", pathMapper, "pathMapper", indexName, "indexName"); try { Index index = graphDatabase.getIndex(indexName); if (Relationship.class.isAssignableFrom(index.getEntityType())) { @@ -191,6 +193,10 @@ public class Neo4jTemplate implements Neo4jOperations { } } + private QueryEngine queryEngineFor(EmbeddedQueryEngine.Type type) { + return graphDatabase.queryEngineFor(type); + } + private ClosableIterable mapNodes(final IndexHits nodes, final PathMapper pathMapper) { assert nodes != null; assert pathMapper != null; @@ -314,4 +320,34 @@ public class Neo4jTemplate implements Neo4jOperations { indexHits.close(); } } + + @Override + public Iterable> query(QueryEngine.Type engineType, String statement) { + return queryEngineFor(engineType).query(statement); + } + + @Override + public Iterable query(QueryEngine.Type engineType, String statement, Class type) { + return queryEngineFor(engineType).query(statement,type); + } + + @Override + public T queryForObject(QueryEngine.Type engineType, String statement, Class type) { + return queryEngineFor(engineType).queryForObject(statement,type); + } } + +/* +conversion for all query methods + +-> target type +-> target type + mapper +-> no conversion (or auto-conversion) -> default mapper + +source Path Node Relationship Primitive, DomainObject +Path -> Path, NodePath, RelationshipPath X lastNode() +Node -> endNode(), node, Mapper X NodeEntity +Relationship lastRel(), Mapper relationship X RelEntity +primitive X X X primitive +Map Mapper +*/ \ No newline at end of file diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/GraphRepositoryTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/GraphRepositoryTest.java index aa8dac0dd..16a913b56 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/GraphRepositoryTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/GraphRepositoryTest.java @@ -18,7 +18,6 @@ package org.springframework.data.graph.neo4j.support; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.hamcrest.core.Is; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -38,8 +37,7 @@ import java.util.Map; import static java.util.Arrays.asList; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.*; import static org.junit.internal.matchers.IsCollectionContaining.hasItems; import static org.neo4j.helpers.collection.IteratorUtil.asCollection; import static org.springframework.data.graph.neo4j.Person.persistedPerson; @@ -77,7 +75,7 @@ public class GraphRepositoryTest { @Test @Transactional public void testFindIterableOfPersonWithQueryAnnotation() { - final TestTeam testTeam = new TestTeam(graphDatabaseContext); + final TestTeam testTeam = new TestTeam(); testTeam.createSDGTeam(); Iterable teamMembers = personRepository.findAllTeamMembers(testTeam.sdg); assertThat(asCollection(teamMembers), hasItems(testTeam.michael,testTeam.david,testTeam.emil)); @@ -85,7 +83,7 @@ public class GraphRepositoryTest { @Test @Transactional public void testFindPersonWithQueryAnnotation() { - final TestTeam testTeam = new TestTeam(graphDatabaseContext); + final TestTeam testTeam = new TestTeam(); testTeam.createSDGTeam(); Person boss = personRepository.findBoss(testTeam.michael); assertThat(boss, is(testTeam.emil)); @@ -93,7 +91,7 @@ public class GraphRepositoryTest { @Test @Transactional public void testFindIterableMapsWithQueryAnnotation() { - final TestTeam testTeam = new TestTeam(graphDatabaseContext); + final TestTeam testTeam = new TestTeam(); testTeam.createSDGTeam(); Iterable> teamMembers = personRepository.findAllTeamMemberData(testTeam.sdg); assertThat(asCollection(teamMembers), hasItems(testTeam.simpleRowFor(testTeam.michael,"member"),testTeam.simpleRowFor(testTeam.david,"member"),testTeam.simpleRowFor(testTeam.emil,"member"))); @@ -151,6 +149,20 @@ public class GraphRepositoryTest { assertEquals(p, pById); } + @Test + @Transactional + public void testExists() { + Person p = persistedPerson("Michael", 35); + boolean found = personRepository.exists(p.getNodeId()); + assertTrue("Found persisted entity", found); + } + @Test + @Transactional + public void testDoesntExist() { + boolean found = personRepository.exists(Long.MAX_VALUE-1); + assertFalse("Non existend id isn't foundpo ", found); + } + @Test @Transactional public void testFinderFindByIdNonexistent() { @@ -162,9 +174,9 @@ public class GraphRepositoryTest { @Test @Transactional public void testFinderCount() { - assertEquals((Long)0L, personRepository.count()); + assertEquals(0L, personRepository.count()); Person p = persistedPerson("Michael", 35); - assertEquals((Long)1L, personRepository.count()); + assertEquals(1L, personRepository.count()); } @Test diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/NodeEntityQueryTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/NodeEntityQueryTest.java index e18b08e7c..b675a5af1 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/NodeEntityQueryTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/NodeEntityQueryTest.java @@ -29,7 +29,6 @@ import org.springframework.transaction.annotation.Transactional; import java.util.Collection; import java.util.Map; -import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.internal.matchers.IsCollectionContaining.hasItems; @@ -49,7 +48,7 @@ public class NodeEntityQueryTest { @Before public void setUp() throws Exception { - testTeam = new TestTeam(graphDatabaseContext); + testTeam = new TestTeam(); testTeam.createSDGTeam(); michael = testTeam.michael; } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/SubReferenceNodeTypeRepresentationStrategyTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/SubReferenceNodeTypeRepresentationStrategyTest.java index 6440656c6..5fd0db037 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/SubReferenceNodeTypeRepresentationStrategyTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/SubReferenceNodeTypeRepresentationStrategyTest.java @@ -200,16 +200,16 @@ public class SubReferenceNodeTypeRepresentationStrategyTest { log.warn("Created volvo"); new Toyota().persist(); log.warn("Created volvo"); - assertEquals("Wrong count for Volvo.", (Long)1L, graphRepositoryFactory.createGraphRepository(Volvo.class).count()); - assertEquals("Wrong count for Toyota.", (Long)1L, graphRepositoryFactory.createGraphRepository(Toyota.class).count()); - assertEquals("Wrong count for Car.", (Long)2L, graphRepositoryFactory.createGraphRepository(Car.class).count()); + assertEquals("Wrong count for Volvo.", 1L, graphRepositoryFactory.createGraphRepository(Volvo.class).count()); + assertEquals("Wrong count for Toyota.", 1L, graphRepositoryFactory.createGraphRepository(Toyota.class).count()); + assertEquals("Wrong count for Car.", 2L, graphRepositoryFactory.createGraphRepository(Car.class).count()); } @Test @Transactional public void testCountClasses() { persistedPerson("Michael", 36); persistedPerson("David", 25); - assertEquals("Wrong Person instance count.", (Long)2L, graphRepositoryFactory.createGraphRepository(Person.class).count()); + assertEquals("Wrong Person instance count.", 2L, graphRepositoryFactory.createGraphRepository(Person.class).count()); } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/TestTeam.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/TestTeam.java index f11cd8abc..264c7925b 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/TestTeam.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/TestTeam.java @@ -28,14 +28,12 @@ import java.util.Map; * @since 13.06.11 */ public class TestTeam { - private final GraphDatabaseContext graphDatabaseContext; public Person michael; public Person emil; public Person david; public Group sdg; - public TestTeam(GraphDatabaseContext graphDatabaseContext) { - this.graphDatabaseContext = graphDatabaseContext; + public TestTeam() { } public void createSDGTeam() { diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/query/QueryEngineTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/query/QueryEngineTest.java new file mode 100644 index 000000000..399451984 --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/query/QueryEngineTest.java @@ -0,0 +1,150 @@ +/** + * Copyright 2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.graph.neo4j.support.query; + +import org.junit.Before; +import org.junit.Test; +import org.junit.internal.matchers.IsCollectionContaining; +import org.junit.runner.RunWith; +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.Node; +import org.neo4j.helpers.collection.IteratorUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.convert.ConversionService; +import org.springframework.data.graph.core.GraphDatabase; +import org.springframework.data.graph.core.NodeBacked; +import org.springframework.data.graph.neo4j.Person; +import org.springframework.data.graph.neo4j.Personality; +import org.springframework.data.graph.neo4j.support.DelegatingGraphDatabase; +import org.springframework.data.graph.neo4j.support.GraphDatabaseContext; +import org.springframework.data.graph.neo4j.support.TestTeam; +import org.springframework.data.graph.neo4j.support.node.Neo4jHelper; +import org.springframework.data.graph.neo4j.template.NeoApiTest; +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.Collection; +import java.util.Map; + +import static java.util.Arrays.asList; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +/** + * @author mh + * @since 13.06.11 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = {"classpath:org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml"}) +@Transactional +public class QueryEngineTest { + @Autowired + protected ConversionService conversionService; + @Autowired + private GraphDatabaseContext graphDatabaseContext; + private QueryEngine queryEngine; + private TestTeam testTeam; + private Person michael; + private GraphDatabase graphDatabase; + + @Before + public void setUp() throws Exception { + graphDatabase = createGraphDatabase(); + testTeam = new TestTeam(); + testTeam.createSDGTeam(); + queryEngine = graphDatabase.queryEngineFor(QueryEngine.Type.Cypher); + michael = testTeam.michael; + } + + @BeforeTransaction + public void cleanDb() { + Neo4jHelper.cleanDb(graphDatabaseContext); + } + + protected GraphDatabase createGraphDatabase() throws Exception { + final DelegatingGraphDatabase graphDatabase = new DelegatingGraphDatabase(graphDatabaseContext.getGraphDatabaseService()); + graphDatabase.setConversionService(conversionService); + return graphDatabase; + } + + @Test + @Transactional + public void testQueryList() throws Exception { + final String queryString = String.format("start person=(%d,%d) return person.name, person.age", idFor(michael), idFor(testTeam.david)); + final Collection> result = IteratorUtil.asCollection(queryEngine.query(queryString)); + + assertEquals(asList(testTeam.simpleRowFor(michael,"person"),testTeam.simpleRowFor(testTeam.david,"person")),result); + } + + @Test + public void testQueryListOfTypePerson() throws Exception { + final String queryString = String.format("start person=(name_index,name,\"%s\") match (person) <-[:boss]- (boss) return boss", michael.getName()); + final Collection result = IteratorUtil.asCollection(queryEngine.query(queryString, Node.class)); + + assertEquals(asList(nodeFor(testTeam.emil)),result); + + } + + private Node nodeFor(final NodeBacked entity) { + return entity.getPersistentState(); + } + private long idFor(final NodeBacked entity) { + return entity.getNodeId(); + } + + @Test + public void testQueryOtherTeamMembers() throws Exception { + final String queryString = String.format("start person=(%d) match (person)<-[:persons]-(team)-[:persons]->(member) return member", idFor(michael)); + System.out.println("testTeam = " + testTeam.sdg.getPersons()); + final Collection result = IteratorUtil.asCollection(queryEngine.query(queryString, Node.class)); + + assertThat(result, IsCollectionContaining.hasItems(nodeFor(testTeam.david), nodeFor(testTeam.emil))); + + } + + @Test + public void testQueryAllTeamMembersByTeam() throws Exception { + final String queryString = String.format("start team=(Group,name,\"%s\") match (team)-[:persons]->(member) return member", testTeam.sdg.getName()); + final Collection result = IteratorUtil.asCollection(queryEngine.query(queryString, Node.class)); + + assertThat(result, IsCollectionContaining.hasItems(nodeFor(testTeam.david),nodeFor(testTeam.michael))); + } + + @Test + public void testQueryForObjectAsGroup() throws Exception { + final String queryString = String.format("start person=(name_index,name,\"%s\") match (person) <-[:persons]- (team) return team", michael.getName()); + final Node result = queryEngine.queryForObject(queryString, Node.class); + + assertEquals(nodeFor(testTeam.sdg),result); + } + @Test + public void testQueryForObjectAsString() throws Exception { + final String queryString = String.format("start person=(name_index,name,\"%s\") match (person) <-[:persons]- (team) return team.name", michael.getName()); + final String result = queryEngine.queryForObject(queryString, String.class); + + assertEquals(testTeam.sdg.getName(),result); + } + @Test + public void testQueryForObjectAsEnum() throws Exception { + final String queryString = String.format("start person=(name_index,name,\"%s\") return person.personality", michael.getName()); + final Personality result = queryEngine.queryForObject(queryString, Personality.class); + + assertEquals(michael.getPersonality(),result); + } +} diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/query/QueryExecutorTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/query/QueryExecutorTest.java index 05020dc40..28f57ee7e 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/query/QueryExecutorTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/query/QueryExecutorTest.java @@ -54,7 +54,7 @@ public class QueryExecutorTest { @Before public void setUp() throws Exception { - testTeam = new TestTeam(graphDatabaseContext); + testTeam = new TestTeam(); testTeam.createSDGTeam(); queryExecutor = new QueryExecutor(graphDatabaseContext); michael = testTeam.michael; diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/template/NeoApiTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/template/NeoApiTest.java index b0c61e77b..b4f3a9e60 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/template/NeoApiTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/template/NeoApiTest.java @@ -21,7 +21,9 @@ import org.junit.Before; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.test.ImpermanentGraphDatabase; import org.neo4j.kernel.impl.transaction.SpringTransactionManager; +import org.springframework.core.convert.ConversionService; import org.springframework.data.graph.core.GraphDatabase; +import org.springframework.data.graph.neo4j.fieldaccess.Neo4jConversionServiceFactoryBean; import org.springframework.data.graph.neo4j.support.DelegatingGraphDatabase; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.jta.JtaTransactionManager; @@ -31,16 +33,22 @@ public abstract class NeoApiTest { protected Neo4jTemplate template; protected PlatformTransactionManager transactionManager; private GraphDatabaseService graphDatabaseService; + protected ConversionService conversionService; @Before public void setUp() throws Exception { + conversionService = createConversionService(); graph = createGraphDatabase(); transactionManager = createTransactionManager(); template = new Neo4jTemplate(graph, transactionManager); } + private ConversionService createConversionService() throws Exception { + return new Neo4jConversionServiceFactoryBean().getObject(); + } + protected PlatformTransactionManager createTransactionManager() { return new JtaTransactionManager(new SpringTransactionManager(graphDatabaseService)); diff --git a/spring-data-neo4j/src/test/resources/org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml b/spring-data-neo4j/src/test/resources/org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml index d369aed08..cb5db5dc3 100644 --- a/spring-data-neo4j/src/test/resources/org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml +++ b/spring-data-neo4j/src/test/resources/org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml @@ -82,11 +82,10 @@ + - - - +