@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);
  }
  @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 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() + ")");
        }
      }
    }
  }