in grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jSession.java [448:603]
private void processInsertsForEntity(org.neo4j.driver.Transaction neo4jTransaction, GraphPersistentEntity entity, Map<PersistentEntity, Collection<PendingInsert>> inserts, List<PendingOperation<Object, Serializable>> cascadingOperations) {
final Collection<PendingInsert> entityInserts = inserts.get(entity);
final boolean hasDynamicLabels = entity.hasDynamicLabels();
final EntityReflector reflector = entity.getReflector();
Neo4jMappingContext mappingContext = getMappingContext();
if (!entityInserts.isEmpty()) {
if (hasDynamicLabels) {
buildAndExecuteCreateStatement(entityInserts, entity, cascadingOperations);
} else {
// use UNWIND and FOREACH to batch
StringBuilder batchCypher = new StringBuilder();
final Map<String, Object> params = new HashMap<>(inserts.size());
Map<String, String> associationMerges = new LinkedHashMap<>();
batchCypher.append(entity.getBatchCreateStatement());
Collection<Map<String, Object>> rows = new ArrayList<>();
for (PendingInsert entityInsert : entityInserts) {
EntityAccess entityAccess = entityInsert.getEntityAccess();
if (entityInsert.wasExecuted()) {
processPendingRelationshipUpdates(entity, entityAccess, (Serializable) entityInsert.getNativeKey(), cascadingOperations, false);
cascadingOperations.addAll(entityInsert.getCascadeOperations());
} else {
if (isVetoedAfterPreOperations(entityInsert)) {
continue;
}
if (entity.isRelationshipEntity()) {
RelationshipPersistentEntity relEntity = (RelationshipPersistentEntity) entity;
Relationship relationship = (Relationship) entityAccess.getEntity();
Object type = entityAccess.getProperty(RelationshipPersistentEntity.TYPE);
// if the type hasn't been modified we can batch
if(relEntity.type().equals(type)) {
Object from = entityAccess.getPropertyValue(RelationshipPersistentEntity.FROM);
Serializable id = null;
if (from != null) {
id = relEntity.getFromEntity().getReflector().getIdentifier(from);
}
if(id != null) {
RelationshipUpdateKey updateKey = new RelationshipUpdateKey(id, relEntity.getFrom());
Collection<Serializable> childIds = pendingRelationshipInserts.get(updateKey);
if(childIds != null) {
pendingRelationshipInserts.remove(updateKey);
Map<String, Object> rowData = new LinkedHashMap<>();
LinkedHashMap<String, Object> nodeProperties = new LinkedHashMap<>();
for (PersistentProperty pp : relEntity.getPersistentProperties()) {
if(pp instanceof Simple || pp instanceof Basic || pp instanceof TenantId) {
String propertyName = pp.getName();
if(RelationshipPersistentEntity.TYPE.equals(propertyName)) {
continue;
}
Object v = reflector.getProperty(relationship, propertyName);
if(v != null) {
nodeProperties.put(propertyName, mappingContext.convertToNative(v));
}
}
}
nodeProperties.putAll( relationship.attributes() );
rowData.put(CypherBuilder.PROPS, nodeProperties);
rowData.put(RelationshipPersistentEntity.FROM, id);
rowData.put(RelationshipPersistentEntity.TO, childIds);
rows.add(rowData);
}
}
}
else {
// otherwise process individual inserts
processPendingRelationshipUpdates(entity, entityAccess, (Serializable) entityInsert.getNativeKey(), cascadingOperations, false);
}
} else {
Object parentId = entityAccess.getIdentifier();
if (parentId != null) {
Map<String, Object> nodeProperties = readNodePropertiesForInsert(entityInsert, entity, entity.getPersistentProperties(), entityAccess);
Object obj = entityInsert.getObject();
Map<String, List<Object>> dynamicRelProps = amendMapWithUndeclaredProperties(entity, nodeProperties, obj, getMappingContext());
Map<String, Object> data = new LinkedHashMap<>();
data.put(CypherBuilder.PROPS, nodeProperties);
rows.add(data);
for (Association association : entity.getAssociations()) {
if (association.isBasic()) continue;
boolean isTree = association.isCircular();
if (association.isOwningSide() || isTree) {
RelationshipUpdateKey key = new RelationshipUpdateKey((Serializable) parentId, association);
Collection<Serializable> childIds = pendingRelationshipInserts.get(key);
if (childIds != null) {
GraphPersistentEntity associatedEntity = (GraphPersistentEntity) association.getAssociatedEntity();
String associationName = association.getName();
Collection<PendingInsert> pendingInserts = inserts.get(associatedEntity);
if (pendingInserts != null) {
Collection<Map<String, Object>> childRows = new ArrayList<>();
for (PendingInsert pendingInsert : pendingInserts) {
Serializable childId = (Serializable) pendingInsert.getNativeKey();
if (childIds.contains(childId)) {
boolean wasExecutedBeforeHand = pendingInsert.wasExecuted();
if (wasExecutedBeforeHand || isVetoedAfterPreOperations(pendingInsert)) {
continue;
}
childIds.remove(childId);
Map<String, Object> childProperties = readNodePropertiesForInsert(pendingInsert, associatedEntity, associatedEntity.getPersistentProperties(), pendingInsert.getEntityAccess());
childRows.add(Collections.<String, Object>singletonMap(CypherBuilder.PROPS, childProperties));
cascadingOperations.addAll(pendingInsert.getCascadeOperations());
}
}
if (!childRows.isEmpty()) {
String parentVariable = entity.getVariableId();
associationMerges.put(associationName, associatedEntity.formatBatchCreate(parentVariable, association));
data.put(associationName, childRows);
}
}
}
}
}
processDynamicAssociationsIfNecessary(entity, entityAccess, obj, entityInsert, cascadingOperations, params, dynamicRelProps);
params.remove(Neo4jEntityPersister.DYNAMIC_ASSOCIATION_PARAM);
}
cascadingOperations.addAll(entityInsert.getCascadeOperations());
}
}
}
params.put(entity.getBatchId(), rows);
if (!associationMerges.isEmpty()) {
for (String merge : associationMerges.keySet()) {
batchCypher.append(associationMerges.get(merge));
}
}
if (batchCypher.length() > 0 && !rows.isEmpty()) {
final String finalCypher = batchCypher.toString();
if (log.isDebugEnabled()) {
log.debug("CREATE Cypher [{}] for parameters [{}]", finalCypher, params);
}
neo4jTransaction.run(finalCypher, params);
}
}
}
}