protected Serializable persistEntity()

in grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/engine/NativeEntryEntityPersister.java [797:1135]


    protected Serializable persistEntity(final PersistentEntity persistentEntity, Object obj, boolean isInsert) {
        T tmp = null;
        ProxyFactory proxyFactory = getProxyFactory();
        // if called internally, obj can potentially be a proxy, which won't work.
        obj = proxyFactory.unwrap(obj);
        final NativeEntryModifyingEntityAccess entityAccess = (NativeEntryModifyingEntityAccess) createEntityAccess(persistentEntity, obj, tmp);

        K k = readObjectIdentifier(entityAccess, persistentEntity.getMapping());

        boolean isUpdate = k != null && !isInsert;
        if (isUpdate && !getSession().isDirty(obj)) {
            return (Serializable) k;
        }

        PendingOperation<T, K> pendingOperation;

        SessionImplementor<Object> si = (SessionImplementor<Object>) session;

        if(si.isPendingAlready(obj)) {
            return (Serializable) k;
        }
        else {
            si.registerPending(obj);
        }

        boolean assignedId = isAssignedId(persistentEntity);
        if (isNotUpdateForAssignedId(persistentEntity, obj, isUpdate, assignedId, si)) {
            isUpdate = false;
        }
        String family = getEntityFamily();

        if (!isUpdate) {
            tmp = createNewEntry(family, obj);

            if (!assignedId) {
                k = generateIdentifier(persistentEntity, tmp);
            }

            cacheNativeEntry(persistentEntity, (Serializable) k, tmp);

            pendingOperation = new PendingInsertAdapter<T, K>(persistentEntity, k, tmp, entityAccess) {
                public void run() {
                    K insertResult = executeInsert(persistentEntity, entityAccess, getNativeKey(), getNativeEntry());
                    if(insertResult == null) {
                        setVetoed(true);
                    }
                }
            };

            entityAccess.setProperty(entityAccess.getIdentifierName(), k);
        }
        else {
            tmp = (T) si.getCachedEntry(persistentEntity, (Serializable) k);
            if (tmp == null) {
                tmp = getFromTPCache(persistentEntity, (Serializable) k);
                if (tmp == null) {
                    tmp = retrieveEntry(persistentEntity, family, (Serializable) k);
                }
            }
            if (tmp == null) {
                tmp = createNewEntry(family);
            }

            final T finalTmp = tmp;
            final K finalK = k;
            pendingOperation = new PendingUpdateAdapter<T, K>(persistentEntity, finalK, finalTmp, entityAccess) {
                public void run() {
                    if (cancelUpdate(persistentEntity, entityAccess)) {
                        setVetoed(true);
                        return;
                    }
                    updateEntry(persistentEntity, entityAccess, getNativeKey(), getNativeEntry());
                    updateTPCache(persistentEntity, finalTmp, (Serializable) finalK);
                    firePostUpdateEvent(persistentEntity, entityAccess);
                }
            };
        }

        final T e = tmp;
        entityAccess.setNativeEntry(e);

        final List<PersistentProperty> props = persistentEntity.getPersistentProperties();
        final Map<Association, List<Serializable>> toManyKeys = new HashMap<Association, List<Serializable>>();
        final Map<OneToMany, Serializable> inverseCollectionUpdates = new HashMap<OneToMany, Serializable>();
        final Map<PersistentProperty, Object> toIndex = new HashMap<PersistentProperty, Object>();
        final Map<PersistentProperty, Object> toUnindex = new HashMap<PersistentProperty, Object>();
        entityAccess.setToIndex(toIndex);
        for (PersistentProperty prop : props) {
            PropertyMapping<Property> pm = prop.getMapping();
            final Property mappedProperty = pm.getMappedForm();
            String key = null;
            if (mappedProperty != null) {
                key = mappedProperty.getTargetName();
            }
            if (key == null) key = prop.getName();
            final boolean indexed = isPropertyIndexed(mappedProperty);
            if ((prop instanceof Simple) ) {

                Object propValue = entityAccess.getProperty(prop.getName());
                if(propValue == null && !isUpdate) {
                    continue;
                }

                handleIndexing(isUpdate, e, toIndex, toUnindex, prop, key, indexed, propValue);

                setEntryValue(e, key, propValue);
            }
            else if((prop instanceof Basic)) {
                Basic basic = (Basic) prop;
                CustomTypeMarshaller customTypeMarshaller = basic.getCustomTypeMarshaller();
                if (customTypeMarshaller != null && customTypeMarshaller.supports(getMappingContext())) {
                    Object propValue = entityAccess.getProperty(prop.getName());
                    Object customValue = customTypeMarshaller.write(prop, propValue, e);
                    handleIndexing(isUpdate, e, toIndex, toUnindex, prop, key, indexed, customValue);
                }
                else {
                    Object propValue = entityAccess.getProperty(prop.getName());
                    if(propValue == null && !isUpdate) {
                        continue;
                    }


                    handleIndexing(isUpdate, e, toIndex, toUnindex, prop, key, indexed, propValue);
                    setEntryValue(e, key, propValue);
                }
            }
            else if ((prop instanceof Custom)) {
                CustomTypeMarshaller customTypeMarshaller = ((Custom) prop).getCustomTypeMarshaller();
                if (customTypeMarshaller.supports(getMappingContext())) {
                    Object propValue = entityAccess.getProperty(prop.getName());
                    if(propValue == null && !isUpdate) {
                        continue;
                    }

                    Object customValue = customTypeMarshaller.write(prop, propValue, e);
                    handleIndexing(isUpdate, e, toIndex, toUnindex, prop, key, indexed, customValue);
                    if(customValue == null) {
                        setEntryValue(e, key, null);
                    }
                }
            }
            else if (prop instanceof OneToMany) {
                final OneToMany oneToMany = (OneToMany) prop;

                final Object propValue = entityAccess.getProperty(oneToMany.getName());
                if (propValue instanceof Collection) {
                    Collection associatedObjects = (Collection) propValue;
                    if (isInitializedCollection(associatedObjects)) {
                        PersistentEntity associatedEntity = oneToMany.getAssociatedEntity();
                        if(associatedEntity != null) {
                            EntityPersister associationPersister = (EntityPersister) session.getPersister(associatedEntity);
                            if (associationPersister != null) {
                                PersistentCollection persistentCollection;
                                boolean newCollection = false;
                                if (associatedObjects instanceof PersistentCollection) {
                                    persistentCollection = (PersistentCollection) associatedObjects;
                                }
                                else {
                                    Class associationType = associatedEntity.getJavaClass();
                                    persistentCollection = getPersistentCollection(associatedObjects, associationType);
                                    entityAccess.setPropertyNoConversion(oneToMany.getName(), persistentCollection);
                                    persistentCollection.markDirty();
                                    newCollection = true;
                                }
                                if (persistentCollection.isDirty()) {
                                    persistentCollection.resetDirty();
                                    List<Serializable> keys = associationPersister.persist(associatedObjects);
                                    toManyKeys.put(oneToMany, keys);
                                    if (newCollection ) {
                                        entityAccess.setProperty(oneToMany.getName(), associatedObjects);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else if (prop instanceof ManyToMany) {
                final ManyToMany manyToMany = (ManyToMany) prop;

                final Object propValue = entityAccess.getProperty(manyToMany.getName());
                if (propValue instanceof Collection) {
                    Collection associatedObjects = (Collection) propValue;
                    if (isInitializedCollection(associatedObjects)) {
                        setManyToMany(persistentEntity, obj, e, manyToMany, associatedObjects, toManyKeys);
                    }
                }
            }
            else if (prop instanceof ToOne) {
                ToOne association = (ToOne) prop;
                if (prop instanceof Embedded) {
                    // For embedded properties simply set the entry value, the underlying implementation
                    // will have to store the embedded entity in an appropriate way (as a sub-document in a document store for example)
                    handleEmbeddedToOne(association, key, entityAccess, e);
                }

                else if (association.getAssociatedEntity() !=  null) {
                    final Object associatedObject = entityAccess.getProperty(prop.getName());
                    if (associatedObject != null) {
                        Serializable associationId;
                        NativeEntryEntityPersister associationPersister = (NativeEntryEntityPersister) session.getPersister(associatedObject);
                        if (proxyFactory.isInitialized(associatedObject) && !session.contains(associatedObject) ) {
                            Serializable tempId = associationPersister.getObjectIdentifier(associatedObject);
                            if (tempId == null) {
                                if (association.doesCascade(CascadeType.PERSIST)) {
                                    tempId = session.persist(associatedObject);
                                }
                            }
                            associationId = tempId;
                        } else {
                            associationId = associationPersister.getObjectIdentifier(associatedObject);
                        }

                        // handling of hasOne inverse key
                        if (association.isForeignKeyInChild()) {
                            T cachedAssociationEntry = (T) si.getCachedEntry(association.getAssociatedEntity(), associationId);
                            if (cachedAssociationEntry != null) {
                                if (association.isBidirectional()) {
                                    Association inverseSide = association.getInverseSide();
                                    if (inverseSide != null) {
                                        setEntryValue(cachedAssociationEntry, inverseSide.getName(), formulateDatabaseReference(association.getAssociatedEntity(), inverseSide, (Serializable) k));
                                    } else {
                                        setEntryValue(cachedAssociationEntry, key, formulateDatabaseReference(association.getAssociatedEntity(), inverseSide, (Serializable) k));
                                    }
                                }
                            }

                                if (association.isBidirectional()) {
                                    Association inverseSide = association.getInverseSide();
                                    if (inverseSide != null) {
                                        EntityAccess inverseAccess = createEntityAccess(inverseSide.getOwner(), associatedObject);
                                        inverseAccess.setProperty(inverseSide.getName(), obj);
                                    }
                                }
                                if( association.doesCascade(CascadeType.PERSIST) ) {
                                    associationPersister.persist(associatedObject);
                                }
                        }
                        // handle of standard many-to-one
                        else {
                            if (associationId != null) {
                                if (indexed && doesRequirePropertyIndexing()) {
                                    toIndex.put(prop, associationId);
                                    if (isUpdate) {
                                        Object oldValue = getEntryValue(e, key);
                                        oldValue = oldValue != null ? convertToNativeKey((Serializable) oldValue) : oldValue;

                                        if (oldValue != null && !oldValue.equals(associationId)) {
                                            toUnindex.put(prop, oldValue);
                                        }
                                    }
                                }
                                setEntryValue(e, key, formulateDatabaseReference(persistentEntity, association, associationId));

                                if (association.isBidirectional()) {
                                    Association inverse = association.getInverseSide();
                                    if (inverse instanceof OneToMany) {
                                        inverseCollectionUpdates.put((OneToMany) inverse, associationId);
                                    }
                                    // unwrap the entity in case it is a proxy, since we may need to update the reverse link.
                                    Object inverseEntity = proxyFactory.unwrap(entityAccess.getProperty(association.getName()));
                                    if (inverseEntity != null) {
                                        EntityAccess inverseAccess = createEntityAccess(association.getAssociatedEntity(), inverseEntity);
                                        Object entity = entityAccess.getEntity();
                                        if (inverse instanceof OneToMany) {
                                            Collection existingValues = (Collection) inverseAccess.getProperty(inverse.getName());
                                            if (existingValues == null) {
                                                existingValues = MappingUtils.createConcreteCollection(inverse.getType());
                                                inverseAccess.setProperty(inverse.getName(), existingValues);
                                            }
                                            if (!existingValues.contains(entity))
                                                existingValues.add(entity);
                                        } else if (inverse instanceof ToOne) {
                                            inverseAccess.setProperty(inverse.getName(), entity);
                                        }
                                    }
                                }
                            }
                        }
                    } else {
                        setEntryValue(e, getPropertyKey(prop), null);
                    }
                }
            }
            else if (prop instanceof EmbeddedCollection) {
                handleEmbeddedToMany(entityAccess, e, prop, key);
            }
        }

        // perform pre-indexing (updating the native entry, if supported by this persister).
        updateToManyIndices(e, k, toManyKeys, true);

        if (!isUpdate) {
            // if the identifier is null at this point that means that datastore could not generated an identifer
            // and the identifer is generated only upon insert of the entity

            final K updateId = k;
            PendingOperation postOperation = new PendingOperationAdapter<T, K>(persistentEntity, k, e) {
                public void run() {
                    updateToManyIndices(e, updateId, toManyKeys, false);

                    if (doesRequirePropertyIndexing()) {
                        toIndex.put(persistentEntity.getIdentity(), updateId);
                        updatePropertyIndices(updateId, toIndex, toUnindex);
                    }
                    for (OneToMany inverseCollection : inverseCollectionUpdates.keySet()) {
                        final Serializable primaryKey = inverseCollectionUpdates.get(inverseCollection);
                        final NativeEntryEntityPersister inversePersister = (NativeEntryEntityPersister) session.getPersister(inverseCollection.getOwner());
                        final AssociationIndexer associationIndexer = inversePersister.getAssociationIndexer(e, inverseCollection);
                        associationIndexer.index(primaryKey, updateId);
                    }
                }
            };
            pendingOperation.addCascadeOperation(postOperation);

            // If the key is still null at this point we have to execute the pending operation now to get the key
            if (k == null) {
                PendingOperationExecution.executePendingOperation(pendingOperation);
            }
            else {
                si.addPendingInsert((PendingInsert) pendingOperation);
            }
        }
        else {
            final K updateId = k;

            PendingOperation postOperation = new PendingOperationAdapter<T, K>(persistentEntity, k, e) {
                public void run() {
                    updateToManyIndices(e, updateId, toManyKeys, false);
                    if (doesRequirePropertyIndexing()) {
                        updatePropertyIndices(updateId, toIndex, toUnindex);
                    }
                }
            };
            pendingOperation.addCascadeOperation(postOperation);
            si.addPendingUpdate((PendingUpdate) pendingOperation);
        }
        return (Serializable) k;
    }