Search in sources :

Example 1 with DirtyStateTrackable

use of com.blazebit.persistence.view.spi.type.DirtyStateTrackable 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)

Example 2 with DirtyStateTrackable

use of com.blazebit.persistence.view.spi.type.DirtyStateTrackable in project blaze-persistence by Blazebit.

the class CompositeAttributeFlusher method getDirtyKind.

@Override
public DirtyKind getDirtyKind(Object initial, Object current) {
    if (current == null) {
        if (initial == null) {
            return DirtyKind.NONE;
        }
        return DirtyKind.UPDATED;
    }
    if (initial == null) {
        return DirtyKind.UPDATED;
    }
    DirtyStateTrackable currentObject = (DirtyStateTrackable) current;
    DirtyStateTrackable initialObject = (DirtyStateTrackable) initial;
    // Skip further checks if we detect identity change
    if (initialObject != currentObject && !initialObject.equals(currentObject)) {
        return DirtyKind.UPDATED;
    }
    if (!currentObject.$$_isDirty()) {
        return DirtyKind.NONE;
    }
    long dirty = currentObject.$$_getSimpleDirty();
    Object[] initialState = initialObject.$$_getInitialState();
    Object[] dirtyState = currentObject.$$_getMutableState();
    for (int i = 0; i < initialState.length; i++) {
        long mask = 1L << i;
        if ((dirty & mask) != 0) {
            if (flushers[i].getDirtyKind(initialState[i], dirtyState[i]) != DirtyKind.NONE) {
                return DirtyKind.MUTATED;
            }
        }
    }
    return DirtyKind.NONE;
}
Also used : DirtyStateTrackable(com.blazebit.persistence.view.spi.type.DirtyStateTrackable)

Example 3 with DirtyStateTrackable

use of com.blazebit.persistence.view.spi.type.DirtyStateTrackable in project blaze-persistence by Blazebit.

the class AbstractChangeModel method getImmutableChangeModelList.

@SuppressWarnings("unchecked")
protected final <X> List<? extends ChangeModel<X>> getImmutableChangeModelList(ManagedViewType<?> currentType, Object o, String attributePath, String[] parts, int start) {
    List<ChangeModel<Object>> models;
    if (o instanceof Collection<?>) {
        Collection<DirtyStateTrackable> collection = (Collection<DirtyStateTrackable>) o;
        models = new ArrayList<>(collection.size());
        for (DirtyStateTrackable element : collection) {
            models.addAll(getAllImmutable(currentType, attributePath, element, parts, start + 1));
        }
    } else {
        Map<?, DirtyStateTrackable> map = (Map<?, DirtyStateTrackable>) o;
        models = new ArrayList<>(map.size());
        for (DirtyStateTrackable element : map.values()) {
            models.addAll(getAllImmutable(currentType, attributePath, element, parts, start + 1));
        }
    }
    return (List<? extends ChangeModel<X>>) (List<?>) models;
}
Also used : ChangeModel(com.blazebit.persistence.view.change.ChangeModel) DirtyStateTrackable(com.blazebit.persistence.view.spi.type.DirtyStateTrackable) Collection(java.util.Collection) ArrayList(java.util.ArrayList) List(java.util.List) Map(java.util.Map)

Example 4 with DirtyStateTrackable

use of com.blazebit.persistence.view.spi.type.DirtyStateTrackable in project blaze-persistence by Blazebit.

the class AbstractChangeModel method isChanged.

@SuppressWarnings("unchecked")
protected final boolean isChanged(ManagedViewType<?> elementType, Object initial, Object current, DirtyChecker<?> dirtyChecker, String attributePath) {
    if (current == null) {
        if (initial != null) {
            // If null-ness changed, we are done
            return true;
        }
        // Not changed if initial null and current as well
        return false;
    }
    DirtyStateTrackable currentObject;
    if (current instanceof DirtyStateTrackable) {
        currentObject = (DirtyStateTrackable) current;
        if (initial == current && !currentObject.$$_isDirty()) {
            // Also if the dirty tracker reports that the object isn't dirty, no need for further checks
            return false;
        }
    } else {
        return Objects.equals(initial, current);
    }
    ManagedViewType<?> currentType = elementType;
    DirtyChecker<DirtyStateTrackable> currentChecker = (DirtyChecker<DirtyStateTrackable>) dirtyChecker;
    String[] parts = attributePath.split("\\.");
    int end = parts.length - 1;
    for (int i = 0; i < end; i++) {
        AbstractMethodAttribute<?, ?> attribute = getAttribute(currentType, attributePath, parts[i]);
        currentType = getType(attribute);
        initial = currentObject.$$_getInitialState()[attribute.getDirtyStateIndex()];
        current = currentObject.$$_getMutableState()[attribute.getDirtyStateIndex()];
        // If a source object was changed, we consider it changed
        if (initial == null) {
            return current != null;
        }
        if (current == null) {
            return true;
        }
        if (current instanceof DirtyStateTrackable) {
            currentObject = (DirtyStateTrackable) current;
        } else {
            // If the object is immutable, the target attribute can't be changed
            return false;
        }
        // If the objects are the same and isn't considered dirty, it's considered not changed
        if (initial == currentObject && !currentObject.$$_isDirty()) {
            return false;
        }
        currentChecker = currentChecker.<DirtyStateTrackable>getNestedCheckers(currentObject)[attribute.getDirtyStateIndex()];
    }
    AbstractMethodAttribute<?, ?> attribute = getAttribute(currentType, attributePath, parts[end]);
    DirtyChecker<Object> lastChecker = currentChecker.getNestedCheckers(currentObject)[attribute.getDirtyStateIndex()];
    Object lastInitialObject = currentObject.$$_getInitialState()[attribute.getDirtyStateIndex()];
    Object lastCurrentObject = currentObject.$$_getMutableState()[attribute.getDirtyStateIndex()];
    return lastChecker.getDirtyKind(lastInitialObject, lastCurrentObject) != DirtyChecker.DirtyKind.NONE;
}
Also used : DirtyStateTrackable(com.blazebit.persistence.view.spi.type.DirtyStateTrackable)

Example 5 with DirtyStateTrackable

use of com.blazebit.persistence.view.spi.type.DirtyStateTrackable in project blaze-persistence by Blazebit.

the class AbstractChangeModel method getAll.

@SuppressWarnings("unchecked")
protected final <X> List<? extends ChangeModel<X>> getAll(ManagedViewType<?> elementType, Object object, DirtyChecker<?> dirtyChecker, String attributePath) {
    DirtyStateTrackable currentObject;
    String[] parts = attributePath.split("\\.");
    if (object instanceof DirtyStateTrackable) {
        currentObject = (DirtyStateTrackable) object;
    } else {
        return getAllImmutable(elementType, attributePath, object, parts, 0);
    }
    ManagedViewType<?> currentType = elementType;
    DirtyChecker<DirtyStateTrackable> currentChecker = (DirtyChecker<DirtyStateTrackable>) dirtyChecker;
    if (object == null) {
        return (List<? extends ChangeModel<X>>) (List<?>) Collections.singletonList(getEmptyChangeModel(currentType, attributePath, parts, 0));
    }
    int end = parts.length - 1;
    for (int i = 0; i < end; i++) {
        AbstractMethodAttribute<?, ?> attribute = getAttribute(currentType, attributePath, parts[i]);
        currentType = getType(attribute);
        Object o = currentObject.$$_getMutableState()[attribute.getDirtyStateIndex()];
        currentChecker = currentChecker.<DirtyStateTrackable>getNestedCheckers(currentObject)[attribute.getDirtyStateIndex()];
        if (o == null) {
            return (List<? extends ChangeModel<X>>) (List<?>) Collections.singletonList(getEmptyChangeModel(currentType, attributePath, parts, i + 1));
        } else if (!(o instanceof DirtyStateTrackable)) {
            if (attribute.isCollection()) {
                return getChangeModelList(currentType, o, currentChecker, attributePath, parts, i + 1);
            } else {
                return getAllImmutable(currentType, attributePath, o, parts, i + 1);
            }
        }
        currentObject = (DirtyStateTrackable) o;
    }
    AbstractMethodAttribute<?, ?> lastAttribute = getAttribute(currentType, attributePath, parts[end]);
    return (List<? extends ChangeModel<X>>) (List<?>) Collections.singletonList(getChangeModel(currentObject, lastAttribute, currentChecker));
}
Also used : DirtyStateTrackable(com.blazebit.persistence.view.spi.type.DirtyStateTrackable) ChangeModel(com.blazebit.persistence.view.change.ChangeModel) ArrayList(java.util.ArrayList) List(java.util.List)

Aggregations

DirtyStateTrackable (com.blazebit.persistence.view.spi.type.DirtyStateTrackable)14 ArrayList (java.util.ArrayList)5 MutableStateTrackable (com.blazebit.persistence.view.spi.type.MutableStateTrackable)4 List (java.util.List)4 ChangeModel (com.blazebit.persistence.view.change.ChangeModel)3 DirtyTracker (com.blazebit.persistence.view.spi.type.DirtyTracker)3 Map (java.util.Map)3 RecordingMap (com.blazebit.persistence.view.impl.collection.RecordingMap)2 ViewToEntityMapper (com.blazebit.persistence.view.impl.entity.ViewToEntityMapper)2 EntityViewProxy (com.blazebit.persistence.view.spi.type.EntityViewProxy)2 Collection (java.util.Collection)2 SingularChangeModel (com.blazebit.persistence.view.change.SingularChangeModel)1 RecordingCollection (com.blazebit.persistence.view.impl.collection.RecordingCollection)1 RecordingList (com.blazebit.persistence.view.impl.collection.RecordingList)1 MapViewToEntityMapper (com.blazebit.persistence.view.impl.entity.MapViewToEntityMapper)1 ManagedViewTypeImplementor (com.blazebit.persistence.view.impl.metamodel.ManagedViewTypeImplementor)1 EntityViewUpdater (com.blazebit.persistence.view.impl.update.EntityViewUpdater)1 BasicDirtyTracker (com.blazebit.persistence.view.spi.type.BasicDirtyTracker)1 AbstractMap (java.util.AbstractMap)1 HashMap (java.util.HashMap)1