@Override public Object invokeMethod(String name, Object obj) { Object[] args = obj.getClass().isArray() ? (Object[]) obj : new Object[] {obj}; if (isCriteriaConstructionMethod(name, args)) { initializeQuery(); uniqueResult = false; invokeClosureNode(args[0]); Object result; if (!uniqueResult) { result = query.list(); } else { result = query.singleResult(); } query = null; return result; } MetaMethod metaMethod = getMetaClass().getMetaMethod(name, args); if (metaMethod != null) { return metaMethod.invoke(this, args); } metaMethod = queryMetaClass.getMetaMethod(name, args); if (metaMethod != null) { return metaMethod.invoke(query, args); } if (args.length == 1 && args[0] instanceof Closure) { final PersistentProperty property = persistentEntity.getPropertyByName(name); if (property instanceof Association) { Association association = (Association) property; Query previousQuery = query; PersistentEntity previousEntity = persistentEntity; Query associationQuery = null; try { associationQuery = query.createQuery(property.getName()); if (associationQuery instanceof AssociationQuery) { previousQuery.add((Query.Criterion) associationQuery); } query = associationQuery; persistentEntity = association.getAssociatedEntity(); invokeClosureNode(args[0]); return query; } finally { persistentEntity = previousEntity; query = previousQuery; } } } throw new MissingMethodException(name, getClass(), args); }
@Override protected void deleteEntities( PersistentEntity pe, @SuppressWarnings("rawtypes") Iterable objects) { List<EntityAccess> entityAccesses = new ArrayList<EntityAccess>(); List<Object> ids = new ArrayList<Object>(); Map<PersistentEntity, Collection<Object>> cascades = new HashMap<PersistentEntity, Collection<Object>>(); for (Object obj : objects) { EntityAccess entityAccess = createEntityAccess(pe, obj); if (cancelDelete(pe, entityAccess)) { return; } entityAccesses.add(entityAccess); ids.add(entityAccess.getIdentifier()); // populate cascades for (Association association : pe.getAssociations()) { Object property = entityAccess.getProperty(association.getName()); if (association.isOwningSide() && association.doesCascade(CascadeType.REMOVE) && (property != null)) { PersistentEntity associatedEntity = association.getAssociatedEntity(); Collection<Object> cascadesForPersistentEntity = cascades.get(associatedEntity); if (cascadesForPersistentEntity == null) { cascadesForPersistentEntity = new ArrayList<Object>(); cascades.put(associatedEntity, cascadesForPersistentEntity); } if (association instanceof ToOne) { cascadesForPersistentEntity.add(property); } else { cascadesForPersistentEntity.addAll((Collection<?>) property); } } } } for (Map.Entry<PersistentEntity, Collection<Object>> entry : cascades.entrySet()) { deleteEntities(entry.getKey(), entry.getValue()); } getCypherEngine() .execute( String.format( "MATCH (n:%s) WHERE n.__id__ in {ids} OPTIONAL MATCH (n)-[r]-() DELETE r,n", ((GraphPersistentEntity) pe).getLabel()), Collections.singletonMap("ids", ids)); for (EntityAccess entityAccess : entityAccesses) { firePostDeleteEvent(pe, entityAccess); } }
private void configureAssociation( GrailsDomainClassProperty grailsDomainClassProperty, final Association association) { association.setAssociatedEntity( getMappingContext() .addPersistentEntity(grailsDomainClassProperty.getReferencedPropertyType())); association.setOwningSide(grailsDomainClassProperty.isOwningSide()); String referencedPropertyName = grailsDomainClassProperty.getReferencedPropertyName(); if (referencedPropertyName != null) { association.setReferencedPropertyName(referencedPropertyName); } else { GrailsDomainClassProperty otherSide = grailsDomainClassProperty.getOtherSide(); if (otherSide != null) { association.setReferencedPropertyName(otherSide.getName()); } } }
public void index(final Object primaryKey, final List foreignKeys) { // if the association is a unidirectional one-to-many we store the keys // embedded in the owning entity, otherwise we use a foreign key if (!association.isBidirectional()) { mongoTemplate.execute( new DbCallback<Object>() { public Object doInDB(DB db) throws MongoException, DataAccessException { List dbRefs = new ArrayList(); for (Object foreignKey : foreignKeys) { if (isReference) { dbRefs.add( new DBRef( db, getCollectionName(association.getAssociatedEntity()), foreignKey)); } else { dbRefs.add(foreignKey); } } nativeEntry.put(association.getName(), dbRefs); if (primaryKey != null) { final DBCollection collection = db.getCollection(getCollectionName(association.getOwner())); DBObject query = new BasicDBObject(MONGO_ID_FIELD, primaryKey); collection.update(query, nativeEntry); } return null; } }); } }
private boolean isReference(Association association) { PropertyMapping mapping = association.getMapping(); if (mapping != null) { MongoAttribute attribute = (MongoAttribute) mapping.getMappedForm(); if (attribute != null) { return attribute.isReference(); } } return true; }
@Override protected Object formulateDatabaseReference( PersistentEntity persistentEntity, Association association, Serializable associationId) { DB db = (DB) session.getNativeInterface(); boolean isReference = isReference(association); if (isReference) { return new DBRef(db, getCollectionName(association.getAssociatedEntity()), associationId); } return associationId; }
@Override protected void setEmbeddedCollectionKeys( Association association, EntityAccess embeddedEntityAccess, DBObject embeddedEntry, List<Serializable> keys) { List dbRefs = new ArrayList(); boolean reference = isReference(association); for (Object foreignKey : keys) { if (reference) { dbRefs.add( new DBRef( (DB) session.getNativeInterface(), getCollectionName(association.getAssociatedEntity()), foreignKey)); } else { dbRefs.add(foreignKey); } } embeddedEntry.put(association.getName(), dbRefs); }
@Override protected void deleteEntity(PersistentEntity pe, Object obj) { EntityAccess entityAccess = createEntityAccess(pe, obj); if (cancelDelete(pe, entityAccess)) { return; } for (Association association : pe.getAssociations()) { if (association.isOwningSide() && association.doesCascade(CascadeType.REMOVE)) { log.debug("cascading delete for property " + association.getName()); GraphPersistentEntity otherPersistentEntity = (GraphPersistentEntity) association.getAssociatedEntity(); Object otherSideValue = entityAccess.getProperty(association.getName()); if (association instanceof ToOne) { deleteEntity(otherPersistentEntity, otherSideValue); } else { deleteEntities(otherPersistentEntity, (Iterable) otherSideValue); } } } getCypherEngine() .execute( String.format( "MATCH (n:%s) WHERE n.__id__={id} OPTIONAL MATCH (n)-[r]-() DELETE r,n", ((GraphPersistentEntity) pe).getLabel()), Collections.singletonMap("id", entityAccess.getIdentifier())); firePostDeleteEvent(pe, entityAccess); }
public List query(Object primaryKey) { // for a unidirectional one-to-many we use the embedded keys if (!association.isBidirectional()) { final Object indexed = nativeEntry.get(association.getName()); if (indexed instanceof Collection) { if (indexed instanceof List) return (List) indexed; return new ArrayList((Collection) indexed); } return Collections.emptyList(); } // for a bidirectional one-to-many we use the foreign key to query the inverse side of the // association Association inverseSide = association.getInverseSide(); Query query = session.createQuery(association.getAssociatedEntity().getJavaClass()); query.eq(inverseSide.getName(), primaryKey); query.projections().id(); return query.list(); }
private void persistAssociationsOfEntity( PersistentEntity pe, EntityAccess entityAccess, boolean isUpdate) { Object obj = entityAccess.getEntity(); DirtyCheckable dirtyCheckable = null; if (obj instanceof DirtyCheckable) { dirtyCheckable = (DirtyCheckable) obj; } for (PersistentProperty pp : pe.getAssociations()) { if ((!isUpdate) || ((dirtyCheckable != null) && dirtyCheckable.hasChanged(pp.getName()))) { Object propertyValue = entityAccess.getProperty(pp.getName()); if ((pp instanceof OneToMany) || (pp instanceof ManyToMany)) { Association association = (Association) pp; if (propertyValue != null) { if (association.isBidirectional()) { // Populate other side of bidi for (Object associatedObject : (Iterable) propertyValue) { EntityAccess assocEntityAccess = createEntityAccess(association.getAssociatedEntity(), associatedObject); assocEntityAccess.setProperty(association.getReferencedPropertyName(), obj); } } Iterable targets = (Iterable) propertyValue; persistEntities(association.getAssociatedEntity(), targets); boolean reversed = RelationshipUtils.useReversedMappingFor(association); if (!reversed) { if (!(propertyValue instanceof LazyEnititySet)) { LazyEnititySet les = new LazyEnititySet( entityAccess, association, getMappingContext().getProxyFactory(), getSession()); les.addAll(targets); entityAccess.setProperty(association.getName(), les); } } } } else if (pp instanceof ToOne) { if (propertyValue != null) { ToOne to = (ToOne) pp; if (to.isBidirectional()) { // Populate other side of bidi EntityAccess assocEntityAccess = createEntityAccess(to.getAssociatedEntity(), propertyValue); if (to instanceof OneToOne) { assocEntityAccess.setProperty(to.getReferencedPropertyName(), obj); } else { Collection collection = (Collection) assocEntityAccess.getProperty(to.getReferencedPropertyName()); if (collection == null) { collection = new ArrayList(); assocEntityAccess.setProperty(to.getReferencedPropertyName(), collection); } if (!collection.contains(obj)) { collection.add(obj); } } } persistEntity(to.getAssociatedEntity(), propertyValue); boolean reversed = RelationshipUtils.useReversedMappingFor(to); String relType = RelationshipUtils.relationshipTypeUsedFor(to); if (!reversed) { getSession() .addPendingInsert( new RelationshipPendingInsert( entityAccess, relType, new EntityAccess(to.getAssociatedEntity(), propertyValue), getCypherEngine())); } } } else { throw new IllegalArgumentException( "wtf don't know how to handle " + pp + "(" + pp.getClass() + ")"); } } } }
public PersistentEntity getIndexedEntity() { return association.getAssociatedEntity(); }
@Override public void run() { Map<String, Object> params = new LinkedHashMap<String, Object>(2); final Object parentId = getEntityAccess().getIdentifier(); params.put(CypherBuilder.START, parentId); params.put(CypherBuilder.END, targetIdentifiers); final GraphPersistentEntity graphParent = (GraphPersistentEntity) getEntity(); final GraphPersistentEntity graphChild = (GraphPersistentEntity) association.getAssociatedEntity(); final boolean nativeParent = graphParent.getIdGenerator() == null; String labelsFrom = graphParent.getLabelsAsString(); String labelsTo = graphChild.getLabelsAsString(); final String relMatch; if (association instanceof DynamicToOneAssociation) { LinkedHashMap<String, String> attrs = new LinkedHashMap<String, String>(); attrs.put(SOURCE_TYPE, graphParent.getJavaClass().getSimpleName()); attrs.put(TARGET_TYPE, graphChild.getJavaClass().getSimpleName()); relMatch = Neo4jQuery.matchForAssociation(association, "r", attrs); } else { relMatch = Neo4jQuery.matchForAssociation(association, "r"); } boolean reversed = RelationshipUtils.useReversedMappingFor(association); if (!reversed && (association instanceof ToOne) && isUpdate) { // delete any previous String cypher; if (nativeParent) { cypher = String.format(CYPHER_DELETE_NATIVE_RELATIONSHIP, labelsFrom, relMatch); } else { cypher = String.format(CYPHER_DELETE_RELATIONSHIP, labelsFrom, relMatch); } if (log.isDebugEnabled()) { log.debug("DELETE Cypher [{}] for parameters [{}]", cypher, params); } graphDatabaseService.execute(cypher, Collections.singletonMap(CypherBuilder.START, parentId)); } StringBuilder cypherQuery = new StringBuilder("MATCH (from") .append(labelsFrom) .append("), (to") .append(labelsTo) .append(") WHERE "); if (nativeParent) { cypherQuery.append("ID(from) = {start}"); } else { cypherQuery.append("from.").append(CypherBuilder.IDENTIFIER).append(" = {start}"); } cypherQuery.append(" AND "); if (graphChild.getIdGenerator() == null) { cypherQuery.append(" ID(to) IN {end} "); } else { cypherQuery.append("to.").append(CypherBuilder.IDENTIFIER).append(" IN {end}"); } cypherQuery.append(" MERGE (from)").append(relMatch).append("(to)"); String cypher = cypherQuery.toString(); if (log.isDebugEnabled()) { log.debug("MERGE Cypher [{}] for parameters [{}]", cypher, params); } graphDatabaseService.execute(cypher, params); }