Search in sources :

Example 1 with RecordingMap

use of com.blazebit.persistence.view.impl.collection.RecordingMap in project blaze-persistence by Blazebit.

the class ResetInitialStateSynchronization method afterCompletion.

@Override
@SuppressWarnings("unchecked")
public void afterCompletion(int status) {
    if (status != Status.STATUS_COMMITTED) {
        Map<EntityViewProxy, ViewTransition> objects;
        if (listenerManager.hasPostRollbackListeners()) {
            objects = new IdentityHashMap<>();
        } else {
            objects = null;
        }
        if (coalescedInitialStates != null) {
            for (int i = 0; i < coalescedInitialStates.size(); i += 2) {
                Object[] initialState = coalescedInitialStates.get(i);
                Object[] originalInitialState = coalescedInitialStates.get(i + 1);
                for (int j = 0; j < initialState.length; j++) {
                    initialState[j] = originalInitialState[j];
                }
            }
        }
        if (coalescedRecordingActions != null) {
            for (int i = 0; i < coalescedRecordingActions.size(); i += 4) {
                Object collectionReference = coalescedRecordingActions.get(i);
                Object actionList = coalescedRecordingActions.get(i + 1);
                Map<Object, Object> added = (Map<Object, Object>) coalescedRecordingActions.get(i + 2);
                Map<Object, Object> removed = (Map<Object, Object>) coalescedRecordingActions.get(i + 3);
                if (collectionReference instanceof RecordingCollection<?, ?>) {
                    RecordingCollection<Collection<Object>, Object> collection = (RecordingCollection<Collection<Object>, Object>) collectionReference;
                    collection.setActions((List<CollectionAction<Collection<Object>>>) (List<?>) actionList, added, removed);
                    collection.$$_markDirty(-1);
                } else {
                    Map<Object, Object> addedElements = (Map<Object, Object>) coalescedRecordingActions.get(i + 4);
                    Map<Object, Object> removedElements = (Map<Object, Object>) coalescedRecordingActions.get(i + 5);
                    RecordingMap<Map<Object, Object>, Object, Object> collection = (RecordingMap<Map<Object, Object>, Object, Object>) collectionReference;
                    collection.setActions((List<MapAction<Map<Object, Object>>>) (List<?>) actionList, added, removed, addedElements, removedElements);
                    collection.$$_markDirty(-1);
                    i += 2;
                }
            }
        }
        if (persistedViews != null) {
            for (int i = 0; i < persistedViews.size(); i += 7) {
                MutableStateTrackable view = (MutableStateTrackable) persistedViews.get(i);
                Object newObject = persistedViews.get(i + 1);
                view.$$_setIsNew(true);
                Object id = persistedViews.get(i + 2);
                DirtyTracker parent = (DirtyTracker) persistedViews.get(i + 3);
                int parentIndex = (int) persistedViews.get(i + 4);
                List<Object> readOnlyParents = (List<Object>) persistedViews.get(i + 5);
                if (id != NO_ID_MARKER) {
                    view.$$_setId(id);
                }
                if (parent != null) {
                    // This is not needed as this will happen when rolling back collection actions already
                    if (!(parent instanceof RecordingCollection<?, ?> || parent instanceof RecordingMap<?, ?, ?>)) {
                        parent.$$_replaceAttribute(newObject, parentIndex, view);
                    }
                    for (int j = 0; j < readOnlyParents.size(); j += 2) {
                        DirtyTracker readOnlyParent = (DirtyTracker) readOnlyParents.get(j);
                        int readOnlyParentIndex = (int) readOnlyParents.get(j + 1);
                        readOnlyParent.$$_replaceAttribute(newObject, readOnlyParentIndex, view);
                    }
                }
                view.$$_setDirty((long[]) persistedViews.get(i + 6));
                if (objects != null) {
                    objects.put(view, ViewTransition.PERSIST);
                }
            }
        }
        if (updatedViews != null) {
            for (int i = 0; i < updatedViews.size(); i += 2) {
                MutableStateTrackable view = (MutableStateTrackable) updatedViews.get(i);
                view.$$_setDirty((long[]) updatedViews.get(i + 1));
                if (objects != null) {
                    objects.put(view, ViewTransition.UPDATE);
                }
            }
        }
        if (removedViews != null) {
            for (int i = 0; i < removedViews.size(); i += 4) {
                EntityViewProxy view = (EntityViewProxy) removedViews.get(i);
                if (view instanceof MutableStateTrackable) {
                    MutableStateTrackable removedView = (MutableStateTrackable) view;
                    DirtyTracker parent = (DirtyTracker) removedViews.get(i + 1);
                    if (parent != null) {
                        removedView.$$_setParent(parent, (Integer) removedViews.get(i + 2));
                    }
                    long[] dirtyArray = (long[]) removedViews.get(i + 3);
                    if (dirtyArray != null) {
                        removedView.$$_setDirty(dirtyArray);
                    }
                }
                if (objects != null) {
                    objects.put(view, ViewTransition.REMOVE);
                }
            }
        }
        if (versionedViews != null) {
            for (int i = 0; i < versionedViews.size(); i += 2) {
                MutableStateTrackable view = (MutableStateTrackable) versionedViews.get(i);
                view.$$_setVersion(versionedViews.get(i + 1));
            }
        }
        if (objects != null) {
            PostRollbackInvoker postRollbackInvoker = new PostRollbackInvoker(updateContext, listenerManager, objects);
            TransactionSupport txSupport = updateContext.getEntityViewManager().getService(TransactionSupport.class);
            if (txSupport == null) {
                TransactionAccess transactionAccess = updateContext.getTransactionAccess();
                if (transactionAccess instanceof SynchronizationRegistry) {
                    transactionAccess = ((SynchronizationRegistry) transactionAccess).getTransactionAccess();
                }
                if (transactionAccess instanceof TransactionSupport) {
                    ((TransactionSupport) transactionAccess).transactional(postRollbackInvoker);
                } else {
                    // Log warning that querying doesn't work because we can't run in a new transaction?
                    postRollbackInvoker.run();
                }
            } else {
                txSupport.transactional(postRollbackInvoker);
            }
        }
    } else {
        if (listenerManager.hasPostCommitListeners()) {
            if (persistedViews != null) {
                for (int i = 0; i < persistedViews.size(); i += 7) {
                    listenerManager.invokePostCommit(updateContext, (MutableStateTrackable) persistedViews.get(i), ViewTransition.PERSIST);
                }
            }
            if (updatedViews != null) {
                for (int i = 0; i < updatedViews.size(); i += 2) {
                    listenerManager.invokePostCommit(updateContext, (MutableStateTrackable) updatedViews.get(i), ViewTransition.UPDATE);
                }
            }
            if (removedViews != null) {
                for (int i = 0; i < removedViews.size(); i += 4) {
                    listenerManager.invokePostCommit(updateContext, (EntityViewProxy) removedViews.get(i), ViewTransition.REMOVE);
                }
            }
        }
    }
}
Also used : MutableStateTrackable(com.blazebit.persistence.view.spi.type.MutableStateTrackable) ViewTransition(com.blazebit.persistence.view.ViewTransition) RecordingCollection(com.blazebit.persistence.view.impl.collection.RecordingCollection) TransactionSupport(com.blazebit.persistence.view.spi.TransactionSupport) EntityViewProxy(com.blazebit.persistence.view.spi.type.EntityViewProxy) ArrayList(java.util.ArrayList) List(java.util.List) DirtyTracker(com.blazebit.persistence.view.spi.type.DirtyTracker) RecordingMap(com.blazebit.persistence.view.impl.collection.RecordingMap) MapAction(com.blazebit.persistence.view.impl.collection.MapAction) SynchronizationRegistry(com.blazebit.persistence.view.impl.tx.SynchronizationRegistry) TransactionAccess(com.blazebit.persistence.view.spi.TransactionAccess) CollectionAction(com.blazebit.persistence.view.impl.collection.CollectionAction) RecordingCollection(com.blazebit.persistence.view.impl.collection.RecordingCollection) Collection(java.util.Collection) IdentityHashMap(java.util.IdentityHashMap) Map(java.util.Map) RecordingMap(com.blazebit.persistence.view.impl.collection.RecordingMap)

Example 2 with RecordingMap

use of com.blazebit.persistence.view.impl.collection.RecordingMap in project blaze-persistence by Blazebit.

the class MapAttributeFlusher method mergeAndRequeue.

private boolean mergeAndRequeue(UpdateContext context, RecordingMap recordingCollection, Map<Object, Object> newCollection) {
    EntityManager em = context.getEntityManager();
    Map<Object, Object> queuedElements = null;
    final ViewToEntityMapper keyMapper = mapper.getKeyMapper();
    final ViewToEntityMapper valueMapper = mapper.getValueMapper();
    final boolean flushKey = keyDescriptor.shouldJpaPersistOrMerge();
    final boolean flushValue = elementDescriptor.shouldJpaPersistOrMerge();
    final Iterator<Map.Entry<Object, Object>> iter = getRecordingIterator(newCollection);
    try {
        while (iter.hasNext()) {
            Map.Entry<Object, Object> entry = iter.next();
            Object key = entry.getKey();
            Object value = entry.getValue();
            if (flushKey) {
                key = persistOrMergeKey(context, em, key);
            } else if (keyMapper != null) {
                keyMapper.applyToEntity(context, null, key);
            }
            if (flushValue) {
                value = persistOrMerge(em, value);
            } else if (valueMapper != null) {
                valueMapper.applyToEntity(context, null, value);
            }
            if (key != entry.getKey()) {
                if (queuedElements == null) {
                    queuedElements = new HashMap<>(newCollection.size());
                }
                iter.remove();
                queuedElements.put(key, value);
                if (recordingCollection != null) {
                    recordingCollection.replaceActionElement(entry.getKey(), entry.getValue(), key, value);
                }
            } else if (value != entry.getValue()) {
                entry.setValue(value);
            }
        }
    } finally {
        resetRecordingIterator(newCollection);
    }
    if (queuedElements != null) {
        newCollection.putAll(queuedElements);
    }
    return true;
}
Also used : EntityManager(javax.persistence.EntityManager) MapViewToEntityMapper(com.blazebit.persistence.view.impl.entity.MapViewToEntityMapper) ViewToEntityMapper(com.blazebit.persistence.view.impl.entity.ViewToEntityMapper) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) Map(java.util.Map) IdentityHashMap(java.util.IdentityHashMap) AbstractMap(java.util.AbstractMap) RecordingMap(com.blazebit.persistence.view.impl.collection.RecordingMap)

Example 3 with RecordingMap

use of com.blazebit.persistence.view.impl.collection.RecordingMap in project blaze-persistence by Blazebit.

the class MapAttributeFlusher method mergeCollectionElements.

@Override
protected boolean mergeCollectionElements(UpdateContext context, Object ownerView, Object view, E entity, V value) {
    if (elementFlushers != null) {
        if (flushStrategy == FlushStrategy.ENTITY || context.isForceEntity()) {
            for (CollectionElementAttributeFlusher<E, V> elementFlusher : elementFlushers) {
                elementFlusher.flushEntity(context, entity, ownerView, view, value, null);
            }
        } else {
            for (CollectionElementAttributeFlusher<E, V> elementFlusher : elementFlushers) {
                elementFlusher.flushQuery(context, null, null, null, ownerView, view, value, null, null);
            }
        }
        return !elementFlushers.isEmpty();
    } else {
        // Invocations of JPA merge can change the identity that leads to requeuing into the collection being required
        final boolean needsRequeuing = keyDescriptor.shouldJpaMerge() || value instanceof RecordingMap<?, ?, ?> && elementDescriptor.shouldJpaMerge();
        if (needsRequeuing) {
            if (value instanceof RecordingMap<?, ?, ?>) {
                return mergeAndRequeue(context, (RecordingMap) value, ((RecordingMap) value).getDelegate());
            } else {
                return mergeAndRequeue(context, null, (Map<Object, Object>) (Map<?, ?>) value);
            }
        } else {
            @SuppressWarnings("unchecked") final EntityManager em = context.getEntityManager();
            final ViewToEntityMapper keyMapper = mapper.getKeyMapper();
            final ViewToEntityMapper valueMapper = mapper.getValueMapper();
            final boolean flushKey = keyDescriptor.shouldJpaPersistOrMerge();
            final boolean flushValue = elementDescriptor.shouldJpaPersistOrMerge();
            final Iterator<Map.Entry<Object, Object>> iter = getRecordingIterator(value);
            try {
                while (iter.hasNext()) {
                    Map.Entry<Object, Object> entry = iter.next();
                    Object k = entry.getKey();
                    Object v = entry.getValue();
                    if (flushKey) {
                        persistOrMergeKey(context, em, k);
                    } else if (keyMapper != null) {
                        keyMapper.applyToEntity(context, null, k);
                    }
                    if (v != null) {
                        if (flushValue) {
                            v = persistOrMerge(em, v);
                        } else if (valueMapper != null) {
                            valueMapper.applyToEntity(context, null, v);
                        }
                    }
                    if (v != entry.getValue()) {
                        entry.setValue(v);
                    }
                }
            } finally {
                resetRecordingIterator(value);
            }
            return true;
        }
    }
}
Also used : EntityManager(javax.persistence.EntityManager) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) Map(java.util.Map) IdentityHashMap(java.util.IdentityHashMap) AbstractMap(java.util.AbstractMap) RecordingMap(com.blazebit.persistence.view.impl.collection.RecordingMap) MapViewToEntityMapper(com.blazebit.persistence.view.impl.entity.MapViewToEntityMapper) ViewToEntityMapper(com.blazebit.persistence.view.impl.entity.ViewToEntityMapper) RecordingMap(com.blazebit.persistence.view.impl.collection.RecordingMap)

Example 4 with RecordingMap

use of com.blazebit.persistence.view.impl.collection.RecordingMap in project blaze-persistence by Blazebit.

the class MapAttributeFlusher method flushQuery.

@Override
public Query flushQuery(UpdateContext context, String parameterPrefix, UpdateQueryFactory queryFactory, Query query, Object ownerView, Object view, V current, UnmappedOwnerAwareDeleter ownerAwareDeleter, DirtyAttributeFlusher<?, ?, ?> ownerFlusher) {
    if (!supportsQueryFlush()) {
        throw new UnsupportedOperationException("Query flush not supported for configuration!");
    }
    if (flushOperation != null) {
        if (current instanceof RecordingMap<?, ?, ?>) {
            ((RecordingMap<?, ?, ?>) current).resetActions(context);
        } else {
            List<MapAction<Map<?, ?>>> actions = new ArrayList<>();
            actions.add(new MapClearAction());
            if (current != null && !current.isEmpty()) {
                actions.add(new MapPutAllAction(current, Collections.emptyMap()));
            }
            current = replaceWithRecordingCollection(context, view, current, actions);
        }
        invokeFlushOperation(context, ownerView, view, null, current);
    } else {
        boolean isRecording = current instanceof RecordingMap<?, ?, ?>;
        if (isRecording) {
            RecordingMap<Map<?, ?>, ?, ?> recordingMap = (RecordingMap<Map<?, ?>, ?, ?>) current;
            if (entityAttributeAccessor == null) {
                // We have a correlation mapping here
                recordingMap.resetActions(context);
            }
            Map<Object, Object> embeddables = null;
            if (elementDescriptor.shouldFlushMutations()) {
                if (elementDescriptor.shouldJpaPersistOrMerge()) {
                    mergeAndRequeue(context, recordingMap, (Map<Object, Object>) recordingMap.getDelegate());
                } else if (elementDescriptor.isSubview() && (elementDescriptor.isIdentifiable() || isIndexed())) {
                    embeddables = flushCollectionViewElements(context, current);
                }
            }
            if (entityAttributeAccessor != null && collectionUpdatable) {
                V initial = (V) viewAttributeAccessor.getInitialValue(view);
                if (initial instanceof RecordingMap<?, ?, ?>) {
                    initial = (V) ((RecordingMap) initial).getInitialVersion();
                }
                recordingMap.resetActions(context);
                // If the initial object was null like it happens during full flushing, we can only replace the collection
                flushCollectionOperations(context, ownerView, view, initial, current, embeddables, (FusedMapActions) null, initial != null);
            }
        } else {
            EqualityChecker equalityChecker;
            if (elementDescriptor.isSubview()) {
                equalityChecker = EqualsEqualityChecker.INSTANCE;
            } else {
                equalityChecker = new IdentityEqualityChecker(elementDescriptor.getBasicUserType());
            }
            V initial = (V) viewAttributeAccessor.getInitialValue(view);
            if (initial instanceof RecordingMap<?, ?, ?>) {
                initial = (V) ((RecordingMap) initial).getInitialVersion();
            }
            List<MapAction<Map<Object, Object>>> actions;
            if (initial == null && replaceWithReferenceContents || !elementDescriptor.supportsDeepEqualityCheck() || elementDescriptor.getBasicUserType() != null && !elementDescriptor.getBasicUserType().supportsDeepCloning()) {
                actions = replaceActions(current);
            } else {
                actions = determineCollectionActions(context, initial, current, equalityChecker);
            }
            current = replaceWithRecordingCollection(context, view, current, actions);
            Map<Object, Object> embeddables = null;
            if (elementDescriptor.shouldFlushMutations()) {
                if (elementDescriptor.shouldJpaPersistOrMerge()) {
                    mergeAndRequeue(context, null, (Map<Object, Object>) current);
                } else if (elementDescriptor.isSubview() && (elementDescriptor.isIdentifiable() || isIndexed())) {
                    embeddables = flushCollectionViewElements(context, current);
                }
            }
            if (entityAttributeAccessor != null && collectionUpdatable) {
                // If the initial object was null like it happens during full flushing, we can only replace the collection
                flushCollectionOperations(context, ownerView, view, initial, current, embeddables, (FusedMapActions) null, initial != null);
            }
        }
    }
    return query;
}
Also used : MapPutAllAction(com.blazebit.persistence.view.impl.collection.MapPutAllAction) MapAction(com.blazebit.persistence.view.impl.collection.MapAction) ArrayList(java.util.ArrayList) MapClearAction(com.blazebit.persistence.view.impl.collection.MapClearAction) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) Map(java.util.Map) IdentityHashMap(java.util.IdentityHashMap) AbstractMap(java.util.AbstractMap) RecordingMap(com.blazebit.persistence.view.impl.collection.RecordingMap) RecordingMap(com.blazebit.persistence.view.impl.collection.RecordingMap)

Example 5 with RecordingMap

use of com.blazebit.persistence.view.impl.collection.RecordingMap in project blaze-persistence by Blazebit.

the class CompositeAttributeFlusher method flushEntity.

@Override
@SuppressWarnings("unchecked")
public boolean flushEntity(UpdateContext context, Object entity, Object ownerView, Object view, Object value, Runnable postReplaceListener) {
    if (element != null) {
        value = element;
    }
    if (!(value instanceof MutableStateTrackable)) {
        // Pass-through i.e. read-only id attributes
        for (int i = 0; i < flushers.length; i++) {
            DirtyAttributeFlusher<?, Object, Object> flusher = flushers[i];
            if (flusher != null) {
                flusher.flushEntity(context, entity, ownerView, value, flusher.getViewAttributeAccessor().getValue(value), null);
            }
        }
        return false;
    }
    MutableStateTrackable updatableProxy = (MutableStateTrackable) value;
    // The root object, which is given when view == value, is the exception
    if (context.isRemovedObject(updatableProxy) || !updatableProxy.$$_hasParent() && view != value) {
        return false;
    }
    final boolean shouldPersist = persist == Boolean.TRUE || persist == null && updatableProxy.$$_isNew();
    final boolean doPersist = shouldPersist && persistable;
    final Object oldId = determineOldId(context, updatableProxy, postReplaceListener);
    int parentIndex = updatableProxy.$$_getParentIndex();
    DirtyTracker parent = updatableProxy.$$_getParent();
    RecordingCollection<?, Object> recordingCollection = null;
    RecordingMap<?, Object, Object> recordingMap = null;
    Object removedValue = null;
    Set<Object> removedKeys = null;
    List<Integer> afterPersistFlushers = null;
    List<Integer> deferredFlushers = null;
    boolean successful = false;
    try {
        Object id = updatableProxy.$$_getId();
        if (doPersist) {
            // In case of nested attributes, the entity instance we get is the container of the attribute
            if (!entityClass.isInstance(entity)) {
                entity = entityLoader.toEntity(context, null, null);
            }
            context.invokePrePersist(updatableProxy, entity);
            // Or we are in the elementFlusher case where we don't iterate through the backing collection and thus can operate on the backing collection directly
            if (parent != null) {
                if (parent instanceof RecordingCollection<?, ?> && ((recordingCollection = (RecordingCollection<?, Object>) parent).isHashBased() || persistViewMapper != null)) {
                    if (recordingCollection.getCurrentIterator() == null) {
                        recordingCollection.getDelegate().remove(updatableProxy);
                    } else {
                        recordingCollection.getCurrentIterator().replace();
                    }
                } else if (parent instanceof RecordingMap<?, ?, ?> && (persistViewMapper != null || updatableProxy.$$_getParentIndex() == 1 && (recordingMap = (RecordingMap<?, Object, Object>) parent).isHashBased())) {
                    recordingMap = (RecordingMap<?, Object, Object>) parent;
                    // Parent index 1 in a recording map means it is part of the key
                    if (updatableProxy.$$_getParentIndex() == 1) {
                        if (recordingMap.getCurrentIterator() == null) {
                            removedValue = recordingMap.getDelegate().remove(updatableProxy);
                        } else {
                            removedValue = recordingMap.getCurrentIterator().replace();
                        }
                    } else {
                        if (removedKeys == null) {
                            removedKeys = new HashSet<>();
                        }
                        // Not sure if a creatable view should be allowed to occur multiple times in the map as value..
                        if (recordingMap.getCurrentIterator() == null) {
                            for (Map.Entry<Object, Object> entry : recordingMap.getDelegate().entrySet()) {
                                if (entry.getValue().equals(updatableProxy)) {
                                    removedKeys.add(entry.getKey());
                                }
                            }
                        } else {
                            recordingMap.getCurrentIterator().replaceValue(removedKeys);
                        }
                    }
                }
            }
            // A version 2.0 or 3.0 might improve on this when redesigning for operation queueing
            if (postReplaceListener != null) {
                postReplaceListener.run();
            }
            if (id != null) {
                idFlusher.flushEntity(context, entity, ownerView, updatableProxy, id, null);
            }
            context.getInitialStateResetter().addUpdatedView(updatableProxy);
        } else {
            // In case of nested attributes, the entity instance we get is the container of the attribute
            if ((loadForEntityFlush || viewIdAccessor == null) && !entityClass.isInstance(entity)) {
                entity = entityLoader.toEntity(context, view, id);
            }
            // After Pre-Update the dirtyness could change
            preUpdate(context, updatableProxy);
        }
        Object[] state = updatableProxy.$$_getMutableState();
        boolean wasDirty = false;
        boolean optimisticLock = false;
        if (updatableProxy instanceof DirtyStateTrackable) {
            Object[] initialState = ((DirtyStateTrackable) updatableProxy).$$_getInitialState();
            context.getInitialStateResetter().addState(initialState, initialState.clone());
            for (int i = 0; i < state.length; i++) {
                final DirtyAttributeFlusher<?, Object, Object> flusher = flushers[i];
                if (flusher != null) {
                    if (doPersist && flusher.requiresFlushAfterPersist(state[i])) {
                        if (afterPersistFlushers == null) {
                            afterPersistFlushers = new ArrayList<>();
                        }
                        afterPersistFlushers.add(i);
                        wasDirty = true;
                        optimisticLock |= flusher.isOptimisticLockProtected();
                    } else if (flusher.requiresDeferredFlush(state[i])) {
                        if (deferredFlushers == null) {
                            deferredFlushers = new ArrayList<>();
                        }
                        deferredFlushers.add(i);
                        wasDirty = true;
                        optimisticLock |= flusher.isOptimisticLockProtected();
                    } else {
                        Object newInitialValue = flusher.cloneDeep(value, initialState[i], state[i]);
                        if (flusher.flushEntity(context, entity, ownerView, value, state[i], null)) {
                            wasDirty = true;
                            optimisticLock |= flusher.isOptimisticLockProtected();
                        }
                        initialState[i] = flusher.getNewInitialValue(context, newInitialValue, state[i]);
                    }
                }
            }
        } else {
            for (int i = 0; i < state.length; i++) {
                final DirtyAttributeFlusher<?, Object, Object> flusher = flushers[i];
                if (flusher != null) {
                    if (doPersist && flusher.requiresFlushAfterPersist(state[i])) {
                        if (afterPersistFlushers == null) {
                            afterPersistFlushers = new ArrayList<>();
                        }
                        afterPersistFlushers.add(i);
                        wasDirty = true;
                        optimisticLock |= flusher.isOptimisticLockProtected();
                    } else if (flusher.requiresDeferredFlush(state[i])) {
                        if (deferredFlushers == null) {
                            deferredFlushers = new ArrayList<>();
                        }
                        deferredFlushers.add(i);
                        wasDirty = true;
                        optimisticLock |= flusher.isOptimisticLockProtected();
                    } else {
                        if (flusher.flushEntity(context, entity, ownerView, value, state[i], null)) {
                            wasDirty = true;
                            optimisticLock |= flusher.isOptimisticLockProtected();
                        }
                    }
                }
            }
        }
        // Pass through flushers
        for (int i = state.length; i < flushers.length; i++) {
            final DirtyAttributeFlusher<?, Object, Object> flusher = flushers[i];
            if (flusher != null) {
                if (flusher.flushEntity(context, entity, ownerView, value, flusher.getViewAttributeAccessor().getValue(value), null)) {
                    wasDirty = true;
                    optimisticLock |= flusher.isOptimisticLockProtected();
                }
            }
        }
        if (versionFlusher != null && optimisticLockProtected && optimisticLock) {
            context.getInitialStateResetter().addVersionedView(updatableProxy, updatableProxy.$$_getVersion());
            // We might have to load the entity for optimistic locking
            if (!entityClass.isInstance(entity)) {
                entity = entityLoader.toEntity(context, view, id);
            }
            versionFlusher.flushEntity(context, entity, ownerView, value, updatableProxy.$$_getVersion(), null);
        }
        if (deferredFlushers != null) {
            deferredFlushEntity(context, entity, ownerView, updatableProxy, deferredFlushers);
        }
        if (doPersist) {
            // If the class of the object is an entity, we persist the object
            context.getEntityManager().persist(entity);
            id = createViewIdByEntityId(entityLoader.getEntityId(context, entity));
            viewIdAccessor.setValue(updatableProxy, id);
        }
        successful = true;
        return wasDirty;
    } finally {
        int newObjectIndex = -1;
        if (shouldPersist) {
            if (idFlusher == null) {
                // Embeddables don't have an id
                newObjectIndex = context.getInitialStateResetter().addPersistedView(updatableProxy);
            } else {
                newObjectIndex = context.getInitialStateResetter().addPersistedView(updatableProxy, oldId);
            }
            if (successful && afterPersistFlushers != null) {
                deferredFlushEntity(context, entity, ownerView, updatableProxy, afterPersistFlushers);
            }
        } else if (successful && afterPersistFlushers != null) {
            deferredFlushEntity(context, entity, ownerView, updatableProxy, afterPersistFlushers);
        }
        Object newObject = null;
        if (doPersist) {
            newObject = updatableProxy;
            if (persistViewMapper != null) {
                newObject = persistViewMapper.map(newObject, context.getEntityViewManager().getOptionalParameters());
                context.getInitialStateResetter().addPersistedViewNewObject(newObjectIndex, newObject);
            }
            if (recordingCollection != null && (recordingCollection.isHashBased() || persistViewMapper != null)) {
                // Reset the parent accordingly
                resetParents(updatableProxy, parentIndex, parent, newObject);
                if (recordingCollection.getCurrentIterator() == null) {
                    recordingCollection.getDelegate().add(newObject);
                } else {
                    recordingCollection.getCurrentIterator().add(newObject);
                }
            } else if (recordingMap != null && (persistViewMapper != null || updatableProxy.$$_getParentIndex() == 1 && recordingMap.isHashBased())) {
                // Reset the parent accordingly
                resetParents(updatableProxy, parentIndex, parent, newObject);
                if (updatableProxy.$$_getParentIndex() == 1) {
                    if (recordingMap.getCurrentIterator() == null) {
                        recordingMap.getDelegate().put(newObject, removedValue);
                    } else {
                        recordingMap.getCurrentIterator().add(newObject, removedValue);
                    }
                } else {
                    for (Object removedKey : removedKeys) {
                        if (recordingMap.getCurrentIterator() == null) {
                            recordingMap.getDelegate().put(removedKey, newObject);
                        } else {
                            recordingMap.getCurrentIterator().add(removedKey, newObject);
                        }
                    }
                }
            } else if (parent != null && persistViewMapper != null) {
                // In case of a singular attribute, we replace the mutable state object to signal the parent flusher
                // SubviewAttributeFlusher is the parent, that uses this object for setting the actual and initial state
                ((MutableStateTrackable) parent).$$_getMutableState()[parentIndex] = newObject;
                updatableProxy.$$_unsetParent();
            }
            context.invokePostPersist(updatableProxy, entity);
        } else {
            context.invokePostUpdate(updatableProxy);
        }
    }
}
Also used : MutableStateTrackable(com.blazebit.persistence.view.spi.type.MutableStateTrackable) RecordingCollection(com.blazebit.persistence.view.impl.collection.RecordingCollection) ArrayList(java.util.ArrayList) DirtyStateTrackable(com.blazebit.persistence.view.spi.type.DirtyStateTrackable) DirtyTracker(com.blazebit.persistence.view.spi.type.DirtyTracker) BasicDirtyTracker(com.blazebit.persistence.view.spi.type.BasicDirtyTracker) RecordingMap(com.blazebit.persistence.view.impl.collection.RecordingMap) HashSet(java.util.HashSet)

Aggregations

RecordingMap (com.blazebit.persistence.view.impl.collection.RecordingMap)7 Map (java.util.Map)6 HashMap (java.util.HashMap)5 IdentityHashMap (java.util.IdentityHashMap)5 LinkedHashMap (java.util.LinkedHashMap)5 MapViewToEntityMapper (com.blazebit.persistence.view.impl.entity.MapViewToEntityMapper)4 AbstractMap (java.util.AbstractMap)4 ArrayList (java.util.ArrayList)4 ViewToEntityMapper (com.blazebit.persistence.view.impl.entity.ViewToEntityMapper)3 MapAction (com.blazebit.persistence.view.impl.collection.MapAction)2 RecordingCollection (com.blazebit.persistence.view.impl.collection.RecordingCollection)2 DirtyStateTrackable (com.blazebit.persistence.view.spi.type.DirtyStateTrackable)2 DirtyTracker (com.blazebit.persistence.view.spi.type.DirtyTracker)2 MutableStateTrackable (com.blazebit.persistence.view.spi.type.MutableStateTrackable)2 List (java.util.List)2 EntityManager (javax.persistence.EntityManager)2 EntityMetamodel (com.blazebit.persistence.parser.EntityMetamodel)1 ExtendedManagedType (com.blazebit.persistence.spi.ExtendedManagedType)1 JpaProvider (com.blazebit.persistence.spi.JpaProvider)1 InverseRemoveStrategy (com.blazebit.persistence.view.InverseRemoveStrategy)1