in grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/engine/Neo4jEntityPersister.java [417:627]
protected Object unmarshall(PersistentEntity persistentEntity, Serializable id, Entity node, Map<String, Object> resultData, Map<Association, Object> initializedAssociations) {
if(log.isDebugEnabled()) {
log.debug( "unmarshalling entity [{}] with id [{}], props {}, {}", persistentEntity.getName(), id, node);
}
final Neo4jSession session = getSession();
GraphPersistentEntity graphPersistentEntity = (GraphPersistentEntity) persistentEntity;
Object instance = persistentEntity.newInstance();
EntityAccess entityAccess = session.createEntityAccess(persistentEntity, instance);
entityAccess.setIdentifierNoConversion(id);
PersistentProperty nodeId = graphPersistentEntity.getNodeId();
if( nodeId != null ) {
((GroovyObject)entityAccess.getEntity()).setProperty(nodeId.getName(), node.id());
}
final Object entity = entityAccess.getEntity();
session.cacheInstance(persistentEntity.getJavaClass(), id, entity);
final List<String> nodeProperties = DefaultGroovyMethods.toList(node.keys());
for (PersistentProperty property: entityAccess.getPersistentEntity().getPersistentProperties()) {
String propertyName = property.getName();
if ( (property instanceof Simple) || (property instanceof TenantId) || (property instanceof Basic)) {
// implicitly sets version property as well
if(node.containsKey(propertyName)) {
entityAccess.setProperty(propertyName, node.get(propertyName).asObject());
nodeProperties.remove(propertyName);
}
} else if (property instanceof Association) {
Association association = (Association) property;
final String associationName = association.getName();
if(initializedAssociations.containsKey(association)) {
entityAccess.setPropertyNoConversion(associationName, initializedAssociations.get(association));
continue;
}
final String associationRelKey = associationName + "Rels";
final String associationNodesKey = associationName + "Nodes";
final String associationIdsKey = associationName + "Ids";
// if the node key is present we have an eager fetch, so initialise the association
if(resultData.containsKey(associationNodesKey)) {
final PersistentEntity associatedEntity = association.getAssociatedEntity();
if (association instanceof ToOne) {
final Neo4jEntityPersister associationPersister = session.getEntityPersister(associatedEntity.getJavaClass());
final Iterable<Node> associationNodes = (Iterable<Node>) resultData.get(associationNodesKey);
final Node associationNode = IteratorUtil.singleOrNull(associationNodes);
if(associationNode != null) {
entityAccess.setPropertyNoConversion(
associationName,
associationPersister.unmarshallOrFromCache(associatedEntity, associationNode)
);
}
}
else if(association instanceof ToMany) {
Collection values;
final Class type = association.getType();
final Collection<Object> associationNodes;
boolean isRelationshipEntity = associatedEntity instanceof RelationshipPersistentEntity;
if(isRelationshipEntity && resultData.containsKey(associationRelKey)) {
associationNodes = (Collection<Object>) resultData.get(associationRelKey);
}
else {
associationNodes = (Collection<Object>) resultData.get(associationNodesKey);
}
final Neo4jResultList resultSet = new Neo4jResultList(0, associationNodes.size(), associationNodes.iterator(), session.getEntityPersister(associatedEntity));
if(isRelationshipEntity) {
RelationshipPersistentEntity relEntity = (RelationshipPersistentEntity) associatedEntity;
Association from = relEntity.getFrom();
Association to = relEntity.getTo();
handleRelationshipSide(persistentEntity, resultData, entity, associationNodesKey, resultSet, from);
handleRelationshipSide(persistentEntity, resultData, entity, associationNodesKey, resultSet, to);
}
else if(association.isBidirectional()) {
final Association inverseSide = association.getInverseSide();
if(inverseSide instanceof ToOne) {
resultSet.setInitializedAssociations(Collections.singletonMap(
inverseSide, entity
));
}
}
if(List.class.isAssignableFrom(type)) {
values = new Neo4jList(entityAccess, association, resultSet, session);
}
else if(SortedSet.class.isAssignableFrom(type)) {
values = new Neo4jSortedSet(entityAccess, association, new TreeSet<>(resultSet), session);
}
else {
values = new Neo4jSet(entityAccess, association, new HashSet<>(resultSet), session);
}
entityAccess.setPropertyNoConversion(propertyName, values);
}
}
else if(resultData.containsKey(associationIdsKey)) {
final Object associationValues = resultData.get(associationIdsKey);
List<Serializable> targetIds = Collections.emptyList();
if(associationValues instanceof Collection) {
targetIds = (List<Serializable>) associationValues;
}
if (association instanceof ToOne) {
ToOne toOne = (ToOne) association;
if (!targetIds.isEmpty()) {
Serializable targetId;
try {
targetId = IteratorUtil.singleOrNull(targetIds);
} catch (NoSuchElementException e) {
throw new DataIntegrityViolationException("Single-ended association has more than one associated identifier: " + association);
}
entityAccess.setPropertyNoConversion(propertyName,
getMappingContext().getProxyFactory().createProxy(
this.session,
toOne.getAssociatedEntity().getJavaClass(),
targetId
)
);
}
} else if (association instanceof ToMany) {
Collection values;
final Class type = association.getType();
if(List.class.isAssignableFrom(type)) {
values = new Neo4jPersistentList(targetIds, session, entityAccess, (ToMany) association);
}
else if(SortedSet.class.isAssignableFrom(type)) {
values = new Neo4jPersistentSortedSet(targetIds, session, entityAccess, (ToMany) association);
}
else {
values = new Neo4jPersistentSet(targetIds, session, entityAccess, (ToMany) association);
}
entityAccess.setPropertyNoConversion(propertyName, values);
} else {
throw new IllegalArgumentException("association " + associationName + " is of type " + association.getClass().getSuperclass().getName());
}
}
else {
// No OPTIONAL MATCH specified so the association queries are lazily executed
if(association instanceof ToOne) {
// first check whether the object has already been loaded from the cache
// if a lazy proxy should be created for this association then create it,
// note that this strategy does not allow for null checks
final Neo4jAssociationQueryExecutor associationQueryExecutor = new Neo4jAssociationQueryExecutor(session, association);
if(association.getMapping().getMappedForm().getFetchStrategy() == FetchType.LAZY) {
final Object proxy = getMappingContext().getProxyFactory().createProxy(
this.session,
associationQueryExecutor,
id
);
entityAccess.setPropertyNoConversion(propertyName,
proxy
);
}
else {
final List<Object> results = associationQueryExecutor.query(id);
if(!results.isEmpty()) {
entityAccess.setPropertyNoConversion(propertyName, results.get(0));
}
}
}
else if(association instanceof ToMany) {
Collection values;
final Class type = association.getType();
if(List.class.isAssignableFrom(type)) {
values = new Neo4jPersistentList(id, session, entityAccess, (ToMany) association);
}
else if(SortedSet.class.isAssignableFrom(type)) {
values = new Neo4jPersistentSortedSet(id, session, entityAccess, (ToMany) association);
}
else {
values = new Neo4jPersistentSet(id, session, entityAccess, (ToMany) association);
}
entityAccess.setPropertyNoConversion(propertyName, values);
}
}
} else {
throw new IllegalArgumentException("property " + property.getName() + " is of type " + property.getClass().getSuperclass().getName());
}
}
Map<String,Object> undeclared = new LinkedHashMap<>();
if (!nodeProperties.isEmpty()) {
for (String nodeProperty : nodeProperties) {
if(!nodeProperty.equals(CypherBuilder.IDENTIFIER)) {
undeclared.put(nodeProperty, node.get(nodeProperty).asObject());
}
}
}
final Object obj = entity;
if(!undeclared.isEmpty()) {
if(obj instanceof DynamicAttributes) {
((DynamicAttributes)obj).attributes(undeclared);
}
}
firePostLoadEvent(entityAccess.getPersistentEntity(), entityAccess);
return obj;
}