use of org.neo4j.ogm.metadata.ClassInfo in project neo4j-ogm by neo4j.
the class EntityGraphMapper method getRelationshipBuilder.
/**
* Fetches and initialises an appropriate {@link RelationshipBuilder} for the specified relationship type
* and direction to the supplied domain object, which may be a node or relationship in the graph.
* In the event that the domain object is a {@link RelationshipEntity}, we create a new relationship, collect
* its properties and return a builder associated to the RE's end node instead
*
* @param cypherBuilder the {@link org.neo4j.ogm.cypher.compiler.Compiler}
* @param entity an object representing a node or relationship entity in the graph
* @param directedRelationship the {@link DirectedRelationship} representing the relationship type and direction we want to establish
* @param mapBothDirections whether the nodes should be linked in both directions
* @return The appropriate {@link RelationshipBuilder}
*/
private RelationshipBuilder getRelationshipBuilder(Compiler cypherBuilder, Object entity, DirectedRelationship directedRelationship, boolean mapBothDirections) {
RelationshipBuilder relationshipBuilder;
if (isRelationshipEntity(entity)) {
Long relId = mappingContext.nativeId(entity);
boolean relationshipIsNew = relId < 0;
boolean relationshipEndsChanged = haveRelationEndsChanged(entity, relId);
if (relationshipIsNew || relationshipEndsChanged) {
relationshipBuilder = cypherBuilder.newRelationship(directedRelationship.type());
if (relationshipEndsChanged) {
// since this relationship will get recreated and all properties will be copied in the process,
// we have to reset the control fields for version and identifier.
FieldInfo versionField = metaData.classInfo(entity).getVersionField();
if (versionField != null) {
versionField.write(entity, null);
}
EntityUtils.setIdentity(entity, null, metaData);
}
} else {
relationshipBuilder = cypherBuilder.existingRelationship(relId, directedRelationship.direction(), directedRelationship.type(), mappingContext.isDirty(entity));
this.mappingContext.getSnapshotOf(entity).ifPresent(snapshot -> relationshipBuilder.setPreviousCompositeProperties(snapshot.getDynamicCompositeProperties()));
}
} else {
relationshipBuilder = cypherBuilder.newRelationship(directedRelationship.type(), mapBothDirections);
}
relationshipBuilder.direction(directedRelationship.direction());
if (isRelationshipEntity(entity)) {
// indicates that this relationship type can be mapped multiple times between 2 nodes
relationshipBuilder.setSingleton(false);
relationshipBuilder.setReference(mappingContext.nativeId(entity));
relationshipBuilder.setRelationshipEntity(true);
ClassInfo classInfo = metaData.classInfo(entity);
if (classInfo.primaryIndexField() != null) {
relationshipBuilder.setPrimaryIdName(classInfo.primaryIndexField().propertyName());
}
}
return relationshipBuilder;
}
use of org.neo4j.ogm.metadata.ClassInfo in project neo4j-ogm by neo4j.
the class EntityGraphMapper method mapEntity.
/**
* Builds Cypher to save the specified object and all its composite objects into the graph database.
*
* @param entity The object to persist into the graph database as a node
* @return The "root" node of the object graph that matches
*/
private NodeBuilder mapEntity(Object entity, int horizon) {
// if this object is transient it won't have a classinfo, and isn't persistable
ClassInfo classInfo = metaData.classInfo(entity);
if (classInfo == null) {
return null;
}
CompileContext context = compiler.context();
NodeBuilder nodeBuilder = context.visitedNode(entity);
if (context.visited(entity, horizon)) {
LOGGER.debug("already visited: {}", entity);
return nodeBuilder;
}
if (nodeBuilder == null) {
nodeBuilder = newNodeBuilder(entity, horizon);
if (!isWriteProtected(WriteProtectionTarget.PROPERTIES, entity)) {
updateNode(entity, context, nodeBuilder);
}
}
if (horizon != 0) {
mapEntityReferences(entity, nodeBuilder, horizon - 1);
} else {
LOGGER.debug("at horizon 0: {} ", entity);
}
return nodeBuilder;
}
use of org.neo4j.ogm.metadata.ClassInfo in project neo4j-ogm by neo4j.
the class EntityGraphMapper method mapRelatedEntity.
/**
* Attempts to build a simple directed relationship in the graph between
* two objects represented as srcEntity and tgtEntity. This function recursively calls mapEntity on the
* target entity first before attempting to create the relationship. In this way, the object graph
* is traversed in depth-first order, and the relationships between the leaf nodes are created
* first.
*
* @param srcNodeBuilder a {@link NodeBuilder} that knows how to create cypher phrases about nodes
* @param relationshipBuilder a {@link RelationshipBuilder} that knows how to create cypher phrases about relationships
* @param horizon a value representing how deep we are mapping
* @param relNodes {@link EntityGraphMapper.RelationshipNodes} representing the nodes at the end of this relationship
*/
private void mapRelatedEntity(NodeBuilder srcNodeBuilder, RelationshipBuilder relationshipBuilder, int level, int horizon, RelationshipNodes relNodes) {
// context.visited fails if the class isn't a mapped class, so we have to check this first, even if mapEntity will do it again
ClassInfo classInfo = metaData.classInfo(relNodes.target);
if (classInfo == null) {
return;
}
CompileContext context = compiler.context();
boolean alreadyVisitedNode = context.visited(relNodes.target, horizon);
boolean selfReferentialUndirectedRelationship = relationshipBuilder.hasDirection(Direction.UNDIRECTED) && relNodes.source.getClass() == relNodes.target.getClass();
boolean relationshipFromExplicitlyMappedObject = level == 1;
// Map this entity (mapEntity checks whether the entity has been visited before)
NodeBuilder tgtNodeBuilder = mapEntity(relNodes.target, horizon);
// - or the relationships is defined on an object being explicitly mapped
if (!alreadyVisitedNode || !selfReferentialUndirectedRelationship || relationshipFromExplicitlyMappedObject) {
LOGGER.debug("trying to map relationship between {} and {}", relNodes.source, relNodes.target);
relNodes.targetId = mappingContext.nativeId(relNodes.target);
updateRelationship(context, srcNodeBuilder, tgtNodeBuilder, relationshipBuilder, relNodes);
}
}
use of org.neo4j.ogm.metadata.ClassInfo in project neo4j-ogm by neo4j.
the class EntityGraphMapper method bothWayMappingRequired.
/**
* Determines whether or not a two way mapping is required for the relationship.
* Relationships annotated with either {@link Relationship} direction INCOMING or OUTGOING and defined between two entities of the same type
* will be considered for a dual mapping.
* Specifically, if the source and target entity are of the same type, and the related object from the source for relationship type R in direction D
* is the same as the related object from the target for relationship type R in direction D, then the relationship is mapped both ways.
*
* @param srcObject the domain object representing the start node of the relationship
* @param relationshipType the type of the relationship from the srcObject
* @param tgtObject the domain object representing the end node of the relationship
* @param relationshipDirection the direction of the relationship from the srcObject
* @return true if the relationship should be mapped both ways, false otherwise
*/
private boolean bothWayMappingRequired(Object srcObject, String relationshipType, Object tgtObject, Relationship.Direction relationshipDirection) {
boolean mapBothWays = false;
ClassInfo tgtInfo = metaData.classInfo(tgtObject);
if (tgtInfo == null) {
LOGGER.warn("Unable to process {} on {}. Check the mapping.", relationshipType, srcObject.getClass());
// #347. attribute is not a rel ? maybe would be better to change FieldInfo.persistableAsProperty ?
return false;
}
for (FieldInfo tgtRelReader : tgtInfo.relationshipFields()) {
Direction tgtRelationshipDirection = tgtRelReader.relationshipDirection();
// and the source must be related to the target and vice versa in the SAME direction
if (tgtRelationshipDirection != Direction.UNDIRECTED && tgtRelReader.relationshipType().equals(relationshipType) && relationshipDirection.equals(tgtRelationshipDirection)) {
Object target = tgtRelReader.read(tgtObject);
mapBothWays = targetEqualsSource(target, srcObject);
}
// We don't need any other field if we already found a match.
if (mapBothWays) {
break;
}
}
return mapBothWays;
}
use of org.neo4j.ogm.metadata.ClassInfo in project neo4j-ogm by neo4j.
the class EntityGraphMapper method deleteObsoleteRelationships.
/**
* Detects object references (including from lists) that have been deleted in the domain.
* These must be persisted as explicit requests to delete the corresponding relationship in the graph
*/
private void deleteObsoleteRelationships() {
CompileContext context = compiler.context();
Map<Long, Object> snapshotOfKnownRelationshipEntities = mappingContext.getSnapshotOfRelationshipEntityRegister();
Iterator<MappedRelationship> mappedRelationshipIterator = mappingContext.getRelationships().iterator();
while (mappedRelationshipIterator.hasNext()) {
MappedRelationship mappedRelationship = mappedRelationshipIterator.next();
// means the user has deleted the relationship
if (!context.removeRegisteredRelationship(mappedRelationship)) {
LOGGER.debug("context-del: {}", mappedRelationship);
// tell the compiler to prepare a statement that will delete the relationship from the graph
RelationshipBuilder builder = compiler.unrelate(mappedRelationship.getStartNodeId(), mappedRelationship.getRelationshipType(), mappedRelationship.getEndNodeId(), mappedRelationship.getRelationshipId());
Object entity = snapshotOfKnownRelationshipEntities.get(mappedRelationship.getRelationshipId());
if (entity != null) {
ClassInfo classInfo = metaData.classInfo(entity);
if (classInfo.hasVersionField()) {
FieldInfo field = classInfo.getVersionField();
builder.setVersionProperty(field.propertyName(), (Long) field.read(entity));
}
}
// remove all nodes that are referenced by this relationship in the mapping context
// this will ensure that stale versions of these objects don't exist
clearRelatedObjects(mappedRelationship.getStartNodeId());
clearRelatedObjects(mappedRelationship.getEndNodeId());
// finally remove the relationship from the mapping context
// mappingContext.removeRelationship(mappedRelationship);
mappedRelationshipIterator.remove();
}
}
}
Aggregations