diff --git a/spring-data-graph-parent/pom.xml b/spring-data-graph-parent/pom.xml index 407193e40..48d5cce02 100644 --- a/spring-data-graph-parent/pom.xml +++ b/spring-data-graph-parent/pom.xml @@ -16,7 +16,7 @@ 1.8.4 1.5.10 3.0.5.RELEASE - 1.0.0.M2 + 1.0.0.BUILD-SNAPSHOT 1.2 1.2-${neo4j.version} diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/annotation/NodeEntity.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/annotation/NodeEntity.java index 79a65e061..2dd847b88 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/annotation/NodeEntity.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/annotation/NodeEntity.java @@ -51,4 +51,11 @@ public @interface NodeEntity { * @return true if the entity is only partially managed by the {@link org.springframework.data.graph.neo4j.support.node.Neo4jNodeBacking} aspect. */ boolean partial() default false; + + + /** + * if set the entity will be attached to the graph store at creation time, otherwise entity.attach() has to be called manually. + * @return + */ + boolean autoAttach() default false; } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DetachableEntityStateAccessors.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DetachableEntityStateAccessors.java index 4242ab39a..33853935a 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DetachableEntityStateAccessors.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DetachableEntityStateAccessors.java @@ -182,6 +182,17 @@ public class DetachableEntityStateAccessors, S public GraphDatabaseContext getGraphDatabaseContext() { return graphDatabaseContext; } + + @Override + public ENTITY attach() { + if (graphDatabaseContext.transactionIsRunning()) { + return delegate.attach(); + } else { + log.warn("New Nodebacked tried to attach outside of transaction " + delegate.getEntity().getClass()); + return getEntity(); + } + } + } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/EntityStateAccessors.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/EntityStateAccessors.java index 5d2388efe..046e7210d 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/EntityStateAccessors.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/EntityStateAccessors.java @@ -57,4 +57,6 @@ public interface EntityStateAccessors,STATE> { boolean hasUnderlyingState(); STATE getUnderlyingState(); + + ENTITY attach(); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/JpaIdFieldAccessListenerFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/JpaIdFieldAccessListenerFactory.java index 1cae1e8a4..92ba47b72 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/JpaIdFieldAccessListenerFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/JpaIdFieldAccessListenerFactory.java @@ -47,7 +47,7 @@ public class JpaIdFieldAccessListenerFactory implements FieldAccessorListenerFac public void valueChanged(NodeBacked nodeBacked, Object oldVal, Object newVal) { if (newVal != null) { EntityStateAccessors stateAccessors=nodeBacked.getStateAccessors(); - stateAccessors.createAndAssignState(); + stateAccessors.attach(); } } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NestedTransactionEntityStateAccessors.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NestedTransactionEntityStateAccessors.java index 667fd388a..07d91e374 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NestedTransactionEntityStateAccessors.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NestedTransactionEntityStateAccessors.java @@ -7,84 +7,89 @@ import org.springframework.data.graph.core.GraphBacked; import org.springframework.data.graph.neo4j.support.GraphDatabaseContext; import java.lang.reflect.Field; +import java.util.concurrent.Callable; public class NestedTransactionEntityStateAccessors, STATE> implements - EntityStateAccessors -{ + EntityStateAccessors { protected final EntityStateAccessors delegate; - private final static Log log = LogFactory.getLog( NestedTransactionEntityStateAccessors.class ); + private final static Log log = LogFactory.getLog(NestedTransactionEntityStateAccessors.class); private GraphDatabaseContext graphDatabaseContext; - public NestedTransactionEntityStateAccessors( final EntityStateAccessors delegate, - GraphDatabaseContext graphDatabaseContext ) - { + public NestedTransactionEntityStateAccessors(final EntityStateAccessors delegate, + GraphDatabaseContext graphDatabaseContext) { this.delegate = delegate; this.graphDatabaseContext = graphDatabaseContext; } @Override - public ENTITY getEntity() - { + public ENTITY getEntity() { return delegate.getEntity(); } - public void setUnderlyingState( STATE state ) - { - delegate.setUnderlyingState( state ); + public void setUnderlyingState(STATE state) { + delegate.setUnderlyingState(state); } @Override - public Object getValue( Field field ) - { - return delegate.getValue( field ); + public Object getValue(Field field) { + return delegate.getValue(field); } @Override - public boolean isWritable( Field field ) - { - return delegate.isWritable( field ); + public boolean isWritable(Field field) { + return delegate.isWritable(field); } @Override - public Object setValue( Field field, Object newVal ) - { - Transaction tx = graphDatabaseContext.beginTx(); - - try - { - Object result = delegate.setValue( field, newVal ); - tx.success(); - return result; - } finally - { - tx.finish(); - } + public Object setValue(final Field field, final Object newVal) { + return doInTransaction(new Callable() { + public Object call() throws Exception { + return delegate.setValue(field,newVal); + } + }); } @Override - public void createAndAssignState() - { - Transaction tx = graphDatabaseContext.beginTx(); - - try - { - delegate.createAndAssignState(); - tx.success(); - } finally - { - tx.finish(); - } + public void createAndAssignState() { + doInTransaction(new Callable() { + public Void call() throws Exception { + delegate.createAndAssignState(); + return null; + } + }); } @Override - public boolean hasUnderlyingState() - { + public boolean hasUnderlyingState() { return delegate.hasUnderlyingState(); } @Override - public STATE getUnderlyingState() - { + public STATE getUnderlyingState() { return delegate.getUnderlyingState(); } + + @Override + public ENTITY attach() { + return doInTransaction(new Callable() { + public ENTITY call() throws Exception { + return delegate.attach(); + } + }); + } + + protected T doInTransaction(Callable call) { + Transaction tx = graphDatabaseContext.beginTx(); + try { + T result = call.call(); + tx.success(); + return result; + } catch (Exception e) { + tx.failure(); + if (e instanceof RuntimeException) throw (RuntimeException) e; + throw new RuntimeException(e); + } finally { + tx.finish(); + } + } } \ No newline at end of file diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeEntityStateAccessors.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeEntityStateAccessors.java index 87bb83b44..f400cd058 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeEntityStateAccessors.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeEntityStateAccessors.java @@ -21,6 +21,7 @@ import org.neo4j.graphdb.NotInTransactionException; import org.springframework.dao.InvalidDataAccessResourceUsageException; import org.springframework.data.graph.core.NodeBacked; import org.springframework.data.graph.neo4j.support.GraphDatabaseContext; +import org.springframework.persistence.support.StateProvider; /** * @author Michael Hunger @@ -56,4 +57,16 @@ public class NodeEntityStateAccessors extends Default throw new InvalidDataAccessResourceUsageException("Not in a Neo4j transaction.", e); } } + + @Override + public ENTITY attach() { + Node node = StateProvider.retrieveState(); + if (node != null) { + setUnderlyingState(node); + } else { + createAndAssignState(); + } + return entity; + + } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeEntityStateAccessorsFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeEntityStateAccessorsFactory.java index 7cd20a383..f2fe19b91 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeEntityStateAccessorsFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeEntityStateAccessorsFactory.java @@ -33,18 +33,19 @@ public class NodeEntityStateAccessorsFactory { private NodeDelegatingFieldAccessorFactory nodeDelegatingFieldAccessorFactory; public EntityStateAccessors getEntityStateAccessors(final NodeBacked entity) { - final NodeEntity graphEntityAnnotation = entity.getClass().getAnnotation(NodeEntity.class); - if (graphEntityAnnotation!=null && graphEntityAnnotation.partial()) { - return new DetachableEntityStateAccessors( - new PartialNodeEntityStateAccessors(null, entity, entity.getClass(), graphDatabaseContext, finderFactory), graphDatabaseContext) { + final NodeEntity graphEntityAnnotation = entity.getClass().getAnnotation(NodeEntity.class); // todo cache ?? + boolean autoAttach = graphEntityAnnotation.autoAttach(); + if (graphEntityAnnotation.partial()) { + PartialNodeEntityStateAccessors partialNodeEntityStateAccessors = new PartialNodeEntityStateAccessors(null, entity, entity.getClass(), graphDatabaseContext, finderFactory); + return new DetachableEntityStateAccessors(partialNodeEntityStateAccessors, graphDatabaseContext) { @Override protected boolean transactionIsRunning() { return super.transactionIsRunning() && getId(entity, entity.getClass()) != null; } }; } else { - return new NestedTransactionEntityStateAccessors( - new NodeEntityStateAccessors(null,entity,entity.getClass(), graphDatabaseContext, nodeDelegatingFieldAccessorFactory),graphDatabaseContext); + NodeEntityStateAccessors nodeEntityStateAccessors = new NodeEntityStateAccessors(null, entity, entity.getClass(), graphDatabaseContext, nodeDelegatingFieldAccessorFactory); + return new NestedTransactionEntityStateAccessors(nodeEntityStateAccessors,graphDatabaseContext); } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/PartialNodeEntityStateAccessors.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/PartialNodeEntityStateAccessors.java index 821200ad6..b34b24f8c 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/PartialNodeEntityStateAccessors.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/PartialNodeEntityStateAccessors.java @@ -25,6 +25,7 @@ import org.springframework.data.graph.annotation.RelatedTo; import org.springframework.data.graph.core.NodeBacked; import org.springframework.data.graph.neo4j.finder.FinderFactory; import org.springframework.data.graph.neo4j.support.GraphDatabaseContext; +import org.springframework.persistence.support.StateProvider; import javax.persistence.Id; import java.lang.reflect.Field; @@ -109,7 +110,7 @@ public class PartialNodeEntityStateAccessors extends final Object id = getId(entity,type); if (id == null) return; final String foreignId = createForeignId(id); - IndexHits indexHits = graphDatabaseContext.getNodeIndex(FOREIGN_ID_INDEX).get(FOREIGN_ID, foreignId.toString()); + IndexHits indexHits = graphDatabaseContext.getNodeIndex(FOREIGN_ID_INDEX).get(FOREIGN_ID, foreignId); Node node = indexHits.hasNext() ? indexHits.next() : null; if (node == null) { node = graphDatabaseContext.createNode(); @@ -127,6 +128,17 @@ public class PartialNodeEntityStateAccessors extends } } + @Override + public ENTITY attach() { + Node node = StateProvider.retrieveState(); + if (node != null) { + setUnderlyingState(node); + } else { + createAndAssignState(); + } + return entity; + } + private void persistForeignId(Node node, Object id) { if (!node.hasProperty(FOREIGN_ID) && id != null) { final String foreignId = createForeignId(id); diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/RelationshipEntityStateAccessors.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/RelationshipEntityStateAccessors.java index 178abc1b5..de61e7cf2 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/RelationshipEntityStateAccessors.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/RelationshipEntityStateAccessors.java @@ -86,4 +86,11 @@ public class RelationshipEntityStateAccessors throw new InvalidDataAccessResourceUsageException("Not in a Neo4j transaction.", e); } } + + @Override + public ENTITY attach() { + createAndAssignState(); + return entity; + } + } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/node/Neo4jNodeBacking.aj b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/node/Neo4jNodeBacking.aj index 4fe30d3cf..bb2f5f8b5 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/node/Neo4jNodeBacking.aj +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/node/Neo4jNodeBacking.aj @@ -76,15 +76,13 @@ public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields