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 587384cf2..6636c9b7a 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 @@ -11,15 +11,6 @@ import java.util.Map; * @since 19.02.11 */ public interface Neo4jOperations { - /** - * Executes the callback in a transactional context, throwing an exception in the callback will cause the transaction to be rolled back - * The callback is passed a GraphDatabaseService. - * @param callback for executing graph operations transactionally, not null - * @param return type - * @return whatever the callback chooses to return - * @throws org.springframework.dao.DataAccessException subclasses - */ - T update(GraphCallback callback); /** * Executes the callback in a NON-transactional context. 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 8ae5dc4d0..85a961771 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 @@ -24,11 +24,14 @@ import org.neo4j.graphdb.traversal.TraversalDescription; 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; public class Neo4jTemplate implements Neo4jOperations { + private final boolean useExplictTransactions; + private final GraphDatabaseService graphDatabaseService; private final Neo4jExceptionTranslator exceptionTranslator = new Neo4jExceptionTranslator(); @@ -43,8 +46,33 @@ public class Neo4jTemplate implements Neo4jOperations { } } + /** + * creates a template that only participates in outside transactions, no implicit transactions are started + * @param graphDatabaseService the neo4j graph database + * @return a Neo4jTemplate instance + */ + public static Neo4jTemplate templateWithExplictTransactions(GraphDatabaseService graphDatabaseService) { + return new Neo4jTemplate(graphDatabaseService,true); + } + + /** + * creates a template that creates implicit transactions for its methods, including exec. If an outside transaction + * is running those participate in the outside transaction. + * @param graphDatabaseService the neo4j graph database + */ public Neo4jTemplate(final GraphDatabaseService graphDatabaseService) { + this(graphDatabaseService,false); + } + + /** + * @param graphDatabaseService the neo4j graph database + * @param useExplictTransactions if set the template only participates in outside transactions, + * no internal implicit transactions are started + * @return a Neo4jTemplate instance + */ + public Neo4jTemplate(final GraphDatabaseService graphDatabaseService, boolean useExplictTransactions) { notNull(graphDatabaseService, "graphDatabaseService"); + this.useExplictTransactions = useExplictTransactions; this.graphDatabaseService = graphDatabaseService; index = this.graphDatabaseService.index(); } @@ -53,32 +81,29 @@ public class Neo4jTemplate implements Neo4jOperations { return exceptionTranslator.translateExceptionIfPossible(ex); } - @Override - public T update(final GraphCallback callback) { - notNull(callback, "callback"); - Transaction tx = graphDatabaseService.beginTx(); - try { - T result = exec(callback); - tx.success(); - return result; - } catch (RuntimeException e) { - tx.failure(); - throw e; - } finally { - tx.finish(); + private Transaction beginTx() { + if (useExplictTransactions) { + return new NullTransaction(); } + return graphDatabaseService.beginTx(); } @Override public T exec(final GraphCallback callback) { notNull(callback, "callback"); + Transaction tx = beginTx(); try { - return callback.doWithGraph(graphDatabaseService); + T result = callback.doWithGraph(graphDatabaseService); + tx.success(); + return result; } catch (RuntimeException e) { + tx.failure(); throw translateExceptionIfPossible(e); } catch (Exception e) { - throw new RuntimeException(e); + throw new UncategorizedGraphStoreException("Error executing callback",e); + } finally { + tx.finish(); } } @@ -93,7 +118,7 @@ public class Neo4jTemplate implements Neo4jOperations { @Override public Node createNode(final Map properties, final String... indexFields) { - return update(new GraphCallback() { + return exec(new GraphCallback() { @Override public Node doWithGraph(GraphDatabaseService graph) throws Exception { Node node = graphDatabaseService.createNode(); @@ -135,11 +160,11 @@ public class Neo4jTemplate implements Neo4jOperations { @Override public T index(final String indexName, final T element, final String field, final Object value) { notNull(element, "element", field, "field", value, "value"); - update(new GraphCallback.WithoutResult() { + exec(new GraphCallback.WithoutResult() { @Override public void doWithGraphWithoutResult(GraphDatabaseService graph) throws Exception { - RelationshipIndex relationshipIndex = relationshipIndexAllowsNull(indexName); - if (relationshipIndex != null && element instanceof Relationship) { + if (element instanceof Relationship) { + RelationshipIndex relationshipIndex = relationshipWriteIndex(indexName); relationshipIndex.add((Relationship) element, field, value); } else if (element instanceof Node) { nodeIndex(indexName).add((Node) element, field, value); @@ -151,11 +176,11 @@ public class Neo4jTemplate implements Neo4jOperations { return element; } - private RelationshipIndex relationshipIndexAllowsNull(String indexName) { + private RelationshipIndex relationshipWriteIndex(String indexName) { if (indexName == null) { - return relationshipIndex("relationship"); + return index.forRelationships("relationship"); } - return relationshipIndex(indexName); + return index.forRelationships(indexName); } private RelationshipIndex relationshipIndex(String indexName) { @@ -267,7 +292,7 @@ public class Neo4jTemplate implements Neo4jOperations { @Override public Relationship createRelationship(final Node startNode, final Node endNode, final RelationshipType relationshipType, final Map properties, final String... indexFields) { notNull(startNode, "startNode", endNode, "endNode", relationshipType, "relationshipType", properties, "properties"); - return update(new GraphCallback() { + return exec(new GraphCallback() { @Override public Relationship doWithGraph(GraphDatabaseService graph) throws Exception { Relationship relationship = startNode.createRelationshipTo(endNode, relationshipType); @@ -289,4 +314,21 @@ public class Neo4jTemplate implements Neo4jOperations { } return primitive; } + + private static class NullTransaction implements Transaction { + @Override + public void failure() { + + } + + @Override + public void success() { + + } + + @Override + public void finish() { + + } + } } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/template/Neo4jTemplateApiTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/template/Neo4jTemplateApiTest.java index 50e2ab848..57a6c90ba 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/template/Neo4jTemplateApiTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/template/Neo4jTemplateApiTest.java @@ -83,7 +83,7 @@ public class Neo4jTemplateApiTest { @Test public void shouldExecuteCallbackInTransaction() throws Exception { - Node refNode = template.update(new GraphCallback() { + Node refNode = template.exec(new GraphCallback() { @Override public Node doWithGraph(GraphDatabaseService graph) throws Exception { Node referenceNode = graph.getReferenceNode(); @@ -98,13 +98,13 @@ public class Neo4jTemplateApiTest { @Test public void shouldRollbackTransactionOnException() { try { - template.update(new GraphCallback.WithoutResult() { - @Override - public void doWithGraphWithoutResult(GraphDatabaseService graph) throws Exception { - graph.getReferenceNode().setProperty("test", "shouldRollbackTransactionOnException"); - throw new RuntimeException("please rollback"); - } - }); + template.exec(new GraphCallback.WithoutResult() { + @Override + public void doWithGraphWithoutResult(GraphDatabaseService graph) throws Exception { + graph.getReferenceNode().setProperty("test", "shouldRollbackTransactionOnException"); + throw new RuntimeException("please rollback"); + } + }); } catch(RuntimeException re){ //ignore } @@ -117,7 +117,7 @@ public class Neo4jTemplateApiTest { new TransactionTemplate(tm).execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(final TransactionStatus status) { - template.update(new GraphCallback.WithoutResult() { + template.exec(new GraphCallback.WithoutResult() { @Override public void doWithGraphWithoutResult(GraphDatabaseService graph) throws Exception { graph.getReferenceNode().setProperty("test", "shouldRollbackTransactionOnException"); @@ -141,6 +141,7 @@ public class Neo4jTemplateApiTest { @Test(expected = DataAccessException.class) public void shouldConvertMissingTransactionExceptionToDataAccessException() { + Neo4jTemplate template = new Neo4jTemplate(graphDatabase, true); template.exec(new GraphCallback.WithoutResult() { @Override public void doWithGraphWithoutResult(GraphDatabaseService graph) throws Exception { @@ -150,6 +151,7 @@ public class Neo4jTemplateApiTest { } @Test(expected = DataAccessException.class) public void shouldConvertNotFoundExceptionToDataAccessException() { + Neo4jTemplate template = new Neo4jTemplate(graphDatabase, true); template.exec(new GraphCallback.WithoutResult() { @Override public void doWithGraphWithoutResult(GraphDatabaseService graph) throws Exception { diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/template/Neo4jTemplateTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/template/Neo4jTemplateTest.java index 37139562c..1cb43dedb 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/template/Neo4jTemplateTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/template/Neo4jTemplateTest.java @@ -27,8 +27,9 @@ public class Neo4jTemplateTest extends NeoApiTest { @Test public void testSingleNode() { final Neo4jOperations template = new Neo4jTemplate(graph); - template.update(new GraphCallback() { - public Void doWithGraph(GraphDatabaseService graph) throws Exception { + template.exec(new GraphCallback.WithoutResult() { + @Override + public void doWithGraphWithoutResult(GraphDatabaseService graph) throws Exception { Node refNode = graph.getReferenceNode(); // TODO easy API Node node = graph.createNode(Property._("name", "Test"), Property._("size", 100)); Node node = graph.createNode(); @@ -40,17 +41,15 @@ public class Neo4jTemplateTest extends NeoApiTest { final Node nodeByRelationship = toTestNode.getEndNode(); assertEquals("Test", nodeByRelationship.getProperty("name")); assertEquals(100, nodeByRelationship.getProperty("size")); - return null; } }); - template.update(new GraphCallback() { - public Void doWithGraph(GraphDatabaseService graph) throws Exception { + template.exec(new GraphCallback.WithoutResult() { + public void doWithGraphWithoutResult(GraphDatabaseService graph) throws Exception { Node refNode = graph.getReferenceNode(); final Relationship toTestNode = refNode.getSingleRelationship(HAS, Direction.OUTGOING); final Node nodeByRelationship = toTestNode.getEndNode(); assertEquals("Test", nodeByRelationship.getProperty("name")); assertEquals(100, nodeByRelationship.getProperty("size")); - return null; } }); } @@ -59,7 +58,7 @@ public class Neo4jTemplateTest extends NeoApiTest { public void testRollback() { final Neo4jOperations template = new Neo4jTemplate(graph); try { - template.update(new GraphCallback.WithoutResult() { + template.exec(new GraphCallback.WithoutResult() { @Override public void doWithGraphWithoutResult(GraphDatabaseService graph) throws Exception { Node node = graph.getReferenceNode(); 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 ff698c3e5..d4a939956 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 @@ -26,21 +26,21 @@ public abstract class NeoApiTest { private void clear() { try { - template.update(new GraphCallback() { - public Void doWithGraph(GraphDatabaseService graph) throws Exception { - for (Node node : graph.getAllNodes()) { - for (Relationship relationship : node.getRelationships()) { - relationship.delete(); + template.exec(new GraphCallback() { + public Void doWithGraph(GraphDatabaseService graph) throws Exception { + for (Node node : graph.getAllNodes()) { + for (Relationship relationship : node.getRelationships()) { + relationship.delete(); + } } + Node referenceNode = graph.getReferenceNode(); + for (Node node : graph.getAllNodes()) { + if (node.equals(referenceNode)) continue; + node.delete(); + } + return null; } - Node referenceNode = graph.getReferenceNode(); - for (Node node : graph.getAllNodes()) { - if (node.equals(referenceNode)) continue; - node.delete(); - } - return null; - } - }); + }); } catch(Exception e) { e.printStackTrace(); // ignore diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/template/NeoTraversalTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/template/NeoTraversalTest.java index 5d82e0fef..80d4cfb9a 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/template/NeoTraversalTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/template/NeoTraversalTest.java @@ -24,7 +24,7 @@ public class NeoTraversalTest extends NeoApiTest { @Test public void testSimpleTraverse() { - template.update(new GraphCallback() { + template.exec(new GraphCallback() { public Void doWithGraph(GraphDatabaseService graph) throws Exception { createFamily(); return null;