private Object unmarshall(PersistentEntity persistentEntity, Long id, Map<String, Object> data) {

    log.debug("unmarshalling entity {}, props {}, {}", id, data);
    EntityAccess entityAccess = new EntityAccess(persistentEntity, persistentEntity.newInstance());
    entityAccess.setConversionService(persistentEntity.getMappingContext().getConversionService());
    entityAccess.setIdentifier(id);
    data.remove("__id__");

    for (PersistentProperty property :
        entityAccess.getPersistentEntity().getPersistentProperties()) {

      String propertyName = property.getName();
      if (property instanceof Simple) { // implicitly sets version property as well
        entityAccess.setProperty(propertyName, data.remove(propertyName));
        //            } else if (property instanceof OneToOne) {
        //                log.error("property " + property.getName() + " is of type " +
        // property.getClass().getSuperclass());
      } else if (property instanceof ToOne) {
        ToOne to = (ToOne) property;

        CypherResult cypherResult =
            getSession()
                .getNativeInterface()
                .execute(
                    CypherBuilder.findRelationshipEndpointIdsFor(to),
                    Collections.singletonMap("id", id));

        Map<String, Object> row = IteratorUtil.singleOrNull(cypherResult);
        if (row != null) {
          Long endpointId = (Long) row.get("id");
          entityAccess.setProperty(
              propertyName,
              getMappingContext()
                  .getProxyFactory()
                  .createProxy(session, to.getAssociatedEntity().getJavaClass(), endpointId));
        }
      } else if ((property instanceof OneToMany) || (property instanceof ManyToMany)) {

        LazyEnititySet lazyEnititySet =
            new LazyEnititySet(
                entityAccess,
                (Association) property,
                getMappingContext().getProxyFactory(),
                getSession());
        entityAccess.setProperty(propertyName, lazyEnititySet);

      } else {
        throw new IllegalArgumentException(
            "property $property.name is of type ${property.class.superclass}");
      }
    }

    if (!data.isEmpty()) {
      GroovyObject go = (GroovyObject) (entityAccess.getEntity());
      go.setProperty(Neo4jGormEnhancer.UNDECLARED_PROPERTIES, data);
    }

    firePostLoadEvent(entityAccess.getPersistentEntity(), entityAccess);
    return entityAccess.getEntity();
  }
  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 void initializeClassMapping(
      Class javaClass, MappingContext context, ClassMapping mapping) {
    if (properties.containsKey(javaClass)) {
      return;
    }

    List<PersistentProperty> persistentProperties = new ArrayList<PersistentProperty>();
    Set<Class> owners = new HashSet<Class>();

    properties.put(javaClass, persistentProperties);
    owningEntities.put(javaClass, owners);

    final ClassPropertyFetcher cpf = ClassPropertyFetcher.forClass(javaClass);
    final PersistentEntity owner = getPersistentEntity(javaClass, context, mapping);

    for (PropertyDescriptor propertyDescriptor : cpf.getPropertyDescriptors()) {
      if (propertyDescriptor.getReadMethod() != null
          && propertyDescriptor.getWriteMethod() != null) {
        Field field;
        try {
          field = cpf.getDeclaredField(propertyDescriptor.getName());
        } catch (Exception e) {
          continue;
        }
        if (field != null) {
          if (field.getAnnotation(Basic.class) != null
              || field.getAnnotation(Temporal.class) != null
              || field.getAnnotation(Version.class) != null) {
            persistentProperties.add(
                propertyFactory.createSimple(owner, context, propertyDescriptor));
          } else if (field.getAnnotation(Id.class) != null) {
            identities.put(
                javaClass, propertyFactory.createIdentity(owner, context, propertyDescriptor));
          } else if (field.getAnnotation(Embedded.class) != null) {
            final org.grails.datastore.mapping.model.types.Embedded embeddedProperty =
                propertyFactory.createEmbedded(owner, context, propertyDescriptor);
            embeddedProperty.setAssociatedEntity(
                getOrCreateAssociatedEntity(context, field.getType()));
            persistentProperties.add(embeddedProperty);
          } else if (field.getAnnotation(OneToOne.class) != null) {
            OneToOne one2one = field.getAnnotation(OneToOne.class);

            if (one2one.mappedBy() != null && one2one.targetEntity() != null) {
              owners.add(one2one.targetEntity());
            }
            final ToOne oneToOneProperty =
                propertyFactory.createOneToOne(owner, context, propertyDescriptor);
            oneToOneProperty.setAssociatedEntity(
                getOrCreateAssociatedEntity(context, field.getType()));
            persistentProperties.add(oneToOneProperty);
          } else if (field.getAnnotation(OneToMany.class) != null) {
            OneToMany one2m = field.getAnnotation(OneToMany.class);

            if (one2m.mappedBy() != null && one2m.targetEntity() != null) {
              owners.add(one2m.targetEntity());
            }
            final org.grails.datastore.mapping.model.types.OneToMany oneToManyProperty =
                propertyFactory.createOneToMany(owner, context, propertyDescriptor);
            oneToManyProperty.setAssociatedEntity(
                getOrCreateAssociatedEntity(context, one2m.targetEntity()));
            persistentProperties.add(oneToManyProperty);
          } else if (field.getAnnotation(ManyToMany.class) != null) {
            ManyToMany m2m = field.getAnnotation(ManyToMany.class);

            if (m2m.mappedBy() != null && m2m.targetEntity() != null) {
              owners.add(m2m.targetEntity());
            }
            final org.grails.datastore.mapping.model.types.ManyToMany manyToManyProperty =
                propertyFactory.createManyToMany(owner, context, propertyDescriptor);
            manyToManyProperty.setAssociatedEntity(
                getOrCreateAssociatedEntity(context, m2m.targetEntity()));
            persistentProperties.add(manyToManyProperty);
          } else if (field.getAnnotation(ManyToOne.class) != null) {
            final ToOne manyToOneProperty =
                propertyFactory.createManyToOne(owner, context, propertyDescriptor);
            manyToOneProperty.setAssociatedEntity(
                getOrCreateAssociatedEntity(context, field.getType()));
            persistentProperties.add(manyToOneProperty);
          } else if (field.getAnnotation(Transient.class) == null) {
            persistentProperties.add(
                propertyFactory.createSimple(owner, context, propertyDescriptor));
          }
        }
      }
    }
  }