added attach method to entity(+accessors), still have to work out the transient case
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
<org.mockito.version>1.8.4</org.mockito.version>
|
||||
<org.slf4j.version>1.5.10</org.slf4j.version>
|
||||
<org.springframework.version>3.0.5.RELEASE</org.springframework.version>
|
||||
<data.commons.version>1.0.0.M2</data.commons.version>
|
||||
<data.commons.version>1.0.0.BUILD-SNAPSHOT</data.commons.version>
|
||||
<neo4j.version>1.2</neo4j.version>
|
||||
<neo4j-utils.version>1.2-${neo4j.version}</neo4j-utils.version>
|
||||
</properties>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -182,6 +182,17 @@ public class DetachableEntityStateAccessors<ENTITY extends GraphBacked<STATE>, 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -57,4 +57,6 @@ public interface EntityStateAccessors<ENTITY extends GraphBacked<STATE>,STATE> {
|
||||
|
||||
boolean hasUnderlyingState();
|
||||
STATE getUnderlyingState();
|
||||
|
||||
ENTITY attach();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ENTITY extends GraphBacked<STATE>, STATE> implements
|
||||
EntityStateAccessors<ENTITY, STATE>
|
||||
{
|
||||
EntityStateAccessors<ENTITY, STATE> {
|
||||
protected final EntityStateAccessors<ENTITY, STATE> 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<ENTITY, STATE> delegate,
|
||||
GraphDatabaseContext graphDatabaseContext )
|
||||
{
|
||||
public NestedTransactionEntityStateAccessors(final EntityStateAccessors<ENTITY, STATE> 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<Object>() {
|
||||
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<Void>() {
|
||||
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<ENTITY>() {
|
||||
public ENTITY call() throws Exception {
|
||||
return delegate.attach();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected <T> T doInTransaction(Callable<T> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<ENTITY extends NodeBacked> 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;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,18 +33,19 @@ public class NodeEntityStateAccessorsFactory {
|
||||
private NodeDelegatingFieldAccessorFactory nodeDelegatingFieldAccessorFactory;
|
||||
|
||||
public EntityStateAccessors<NodeBacked,Node> getEntityStateAccessors(final NodeBacked entity) {
|
||||
final NodeEntity graphEntityAnnotation = entity.getClass().getAnnotation(NodeEntity.class);
|
||||
if (graphEntityAnnotation!=null && graphEntityAnnotation.partial()) {
|
||||
return new DetachableEntityStateAccessors<NodeBacked, Node>(
|
||||
new PartialNodeEntityStateAccessors<NodeBacked>(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<NodeBacked> partialNodeEntityStateAccessors = new PartialNodeEntityStateAccessors<NodeBacked>(null, entity, entity.getClass(), graphDatabaseContext, finderFactory);
|
||||
return new DetachableEntityStateAccessors<NodeBacked, Node>(partialNodeEntityStateAccessors, graphDatabaseContext) {
|
||||
@Override
|
||||
protected boolean transactionIsRunning() {
|
||||
return super.transactionIsRunning() && getId(entity, entity.getClass()) != null;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return new NestedTransactionEntityStateAccessors<NodeBacked, Node>(
|
||||
new NodeEntityStateAccessors<NodeBacked>(null,entity,entity.getClass(), graphDatabaseContext, nodeDelegatingFieldAccessorFactory),graphDatabaseContext);
|
||||
NodeEntityStateAccessors<NodeBacked> nodeEntityStateAccessors = new NodeEntityStateAccessors<NodeBacked>(null, entity, entity.getClass(), graphDatabaseContext, nodeDelegatingFieldAccessorFactory);
|
||||
return new NestedTransactionEntityStateAccessors<NodeBacked, Node>(nodeEntityStateAccessors,graphDatabaseContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<ENTITY extends NodeBacked> extends
|
||||
final Object id = getId(entity,type);
|
||||
if (id == null) return;
|
||||
final String foreignId = createForeignId(id);
|
||||
IndexHits<Node> indexHits = graphDatabaseContext.getNodeIndex(FOREIGN_ID_INDEX).get(FOREIGN_ID, foreignId.toString());
|
||||
IndexHits<Node> 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<ENTITY extends NodeBacked> 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);
|
||||
|
||||
@@ -86,4 +86,11 @@ public class RelationshipEntityStateAccessors<ENTITY extends RelationshipBacked>
|
||||
throw new InvalidDataAccessResourceUsageException("Not in a Neo4j transaction.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ENTITY attach() {
|
||||
createAndAssignState();
|
||||
return entity;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -76,15 +76,13 @@ public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields<NodeEnt
|
||||
} else {
|
||||
if (entity.stateAccessors != null) return;
|
||||
entity.stateAccessors = entityStateAccessorsFactory.getEntityStateAccessors(entity);
|
||||
Node node = StateProvider.retrieveState();
|
||||
if (node != null) {
|
||||
entity.setUnderlyingState(node);
|
||||
} else {
|
||||
entity.stateAccessors.createAndAssignState();
|
||||
}
|
||||
entity.attach();
|
||||
}
|
||||
}
|
||||
|
||||
public NodeBacked NodeBacked.attach() {
|
||||
return this.stateAccessors.attach();
|
||||
}
|
||||
/**
|
||||
* State accessors that encapsulate the underlying state and the behaviour related to it (field access, creation)
|
||||
*/
|
||||
|
||||
@@ -54,10 +54,6 @@ public aspect Neo4jRelationshipBacking extends AbstractTypeAnnotatingMixinFields
|
||||
this.entityStateAccessorsFactory = entityStateAccessorsFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* field for underlying relationship
|
||||
*/
|
||||
private Relationship RelationshipBacked.underlyingRelationship;
|
||||
/**
|
||||
* field for {@link EntityStateAccessors} that takes care of all entity operations
|
||||
*/
|
||||
@@ -68,20 +64,18 @@ public aspect Neo4jRelationshipBacking extends AbstractTypeAnnotatingMixinFields
|
||||
* @param r
|
||||
*/
|
||||
public void RelationshipBacked.setUnderlyingState(Relationship r) {
|
||||
this.underlyingRelationship = r;
|
||||
if (this.stateAccessors == null) {
|
||||
this.stateAccessors = Neo4jRelationshipBacking.aspectOf().entityStateAccessorsFactory.getEntityStateAccessors(this);
|
||||
} else {
|
||||
this.stateAccessors.setUnderlyingState(r);
|
||||
}
|
||||
this.stateAccessors.setUnderlyingState(r);
|
||||
}
|
||||
|
||||
public Relationship RelationshipBacked.getUnderlyingState() {
|
||||
return underlyingRelationship;
|
||||
return this.stateAccessors.getUnderlyingState();
|
||||
}
|
||||
|
||||
public boolean RelationshipBacked.hasUnderlyingRelationship() {
|
||||
return underlyingRelationship!=null;
|
||||
return this.stateAccessors.hasUnderlyingState();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,7 +83,7 @@ public aspect Neo4jRelationshipBacking extends AbstractTypeAnnotatingMixinFields
|
||||
*/
|
||||
public Long RelationshipBacked.getId() {
|
||||
if (!hasUnderlyingRelationship()) return null;
|
||||
return underlyingRelationship.getId();
|
||||
return getUnderlyingState().getId();
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user