Skip to content

Commit

Permalink
Allow re-add to map, handle flat view dirty only, make empty embeddab…
Browse files Browse the repository at this point in the history
…le in non-indexed collection null
  • Loading branch information
beikov committed Dec 17, 2018
1 parent 23b96fe commit 13e0be4
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,6 @@ protected final void addAction(MapAction<C> action) {
this.removedElements = new IdentityHashMap<>();
}


// addAction optimizes actions by figuring converting to physical changes
if (optimize) {
action.addAction(actions, addedKeys, removedKeys, addedElements, removedElements);
Expand All @@ -642,7 +641,12 @@ protected final void addAction(MapAction<C> action) {
if (this.removedKeys.remove(o) == null) {
if (this.addedKeys.put((K) o, (K) o) == null) {
if (parent != null && o instanceof BasicDirtyTracker) {
((BasicDirtyTracker) o).$$_setParent(this, 1);
// Check if it was replaced by itself
if (removedKeys.remove(o)) {
this.addedKeys.remove(o);
} else {
((BasicDirtyTracker) o).$$_setParent(this, 1);
}
}
}
} else {
Expand Down Expand Up @@ -670,7 +674,12 @@ protected final void addAction(MapAction<C> action) {
if (this.removedElements.remove(o) == null) {
if (this.addedElements.put((V) o, (V) o) == null) {
if (parent != null && o instanceof BasicDirtyTracker) {
((BasicDirtyTracker) o).$$_setParent(this, 2);
// Check if it was replaced by itself
if (removedElements.remove(o)) {
this.addedElements.remove(o);
} else {
((BasicDirtyTracker) o).$$_setParent(this, 2);
}
}
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ public T map(S source) {
}
}
T result = objectInstantiator.newInstance(tuple);
// TODO: copy initial state, on create new, mark everything as dirty
if (dirtyMapping != null && source instanceof DirtyTracker) {
DirtyTracker oldDirtyTracker = (DirtyTracker) source;
DirtyTracker dirtyTracker = (DirtyTracker) result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,9 @@ private void applyMapping(AbstractAttribute<?, ?> attribute, String parentAttrib
} else {
MappingAttribute<? super T, ?> mappingAttribute = (MappingAttribute<? super T, ?>) attribute;
ManagedViewTypeImplementor<Object[]> managedViewType = (ManagedViewTypeImplementor<Object[]>) pluralAttribute.getElementType();
applySubviewMapping(mappingAttribute, attributePath, newTupleIdDescriptor, managedViewType, mapperBuilder, embeddingViewJpqlMacro, false, managedViewType instanceof ViewType<?>);
// Obviously, we produce null if the object type is identifiable i.e. a ViewType and it is empty = null id
// Additionally, we also consider empty embeddables as null when we have a non-indexed collection so we can filter out these elements
applySubviewMapping(mappingAttribute, attributePath, newTupleIdDescriptor, managedViewType, mapperBuilder, embeddingViewJpqlMacro, false, managedViewType instanceof ViewType<?> || !listKey && !mapKey);
}
} else if (mapKey) {
MappingAttribute<? super T, ?> mappingAttribute = (MappingAttribute<? super T, ?>) attribute;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

Expand Down Expand Up @@ -363,7 +364,17 @@ protected final void persistIfNeeded(EntityManager em, Object object, BasicUserT

protected abstract boolean isIndexed();

protected abstract void addElementFlushActions(UpdateContext context, TypeDescriptor elementDescriptor, List<A> actions, V current);
protected abstract void addFlatViewElementFlushActions(UpdateContext context, TypeDescriptor elementDescriptor, List<A> actions, V current);

protected static boolean identityContains(Collection<Object> addedElements, MutableStateTrackable element) {
for (Object addedElement : addedElements) {
if (addedElement == element) {
return true;
}
}

return false;
}

@SuppressWarnings("unchecked")
protected final DirtyAttributeFlusher<X, E, V> getElementOnlyFlusher(UpdateContext context, V current) {
Expand Down Expand Up @@ -476,7 +487,7 @@ protected final boolean determineElementFlushers(UpdateContext context, TypeDesc
}
} else {
if (typeDescriptor.supportsDirtyCheck() && !typeDescriptor.isIdentifiable() && isIndexed()) {
addElementFlushActions(context, typeDescriptor, (List<A>) actions, current);
addFlatViewElementFlushActions(context, typeDescriptor, (List<A>) actions, current);
} else {
for (Object o : values) {
if (o instanceof MutableStateTrackable) {
Expand Down Expand Up @@ -529,6 +540,49 @@ protected final boolean determineElementFlushers(UpdateContext context, TypeDesc

protected abstract AbstractPluralAttributeFlusher<X, A, R, E, V> partialFlusher(boolean fetch, PluralFlushOperation operation, List<? extends A> collectionActions, List<CollectionElementAttributeFlusher<E, V>> elementFlushers);

/**
* @author Christian Beikov
* @since 1.4.0
*/
protected static enum EntryState {
EXISTED {
@Override
EntryState onAdd() {
return EXISTED;
}

@Override
EntryState onRemove() {
return REMOVED;
}
},
ADDED {
@Override
EntryState onAdd() {
return ADDED;
}

@Override
EntryState onRemove() {
return EXISTED;
}
},
REMOVED {
@Override
EntryState onAdd() {
return EXISTED;
}

@Override
EntryState onRemove() {
return REMOVED;
}
};

abstract EntryState onAdd();
abstract EntryState onRemove();
}

/**
* @author Christian Beikov
* @since 1.2.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,7 @@ protected boolean isIndexed() {
}

@Override
protected void addElementFlushActions(UpdateContext context, TypeDescriptor elementDescriptor, List<CollectionAction<?>> actions, V current) {
protected void addFlatViewElementFlushActions(UpdateContext context, TypeDescriptor elementDescriptor, List<CollectionAction<?>> actions, V current) {
throw new UnsupportedOperationException("Not indexed!");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import com.blazebit.persistence.view.impl.collection.MapAction;
import com.blazebit.persistence.view.impl.entity.ViewToEntityMapper;
import com.blazebit.persistence.view.impl.proxy.DirtyTracker;
import com.blazebit.persistence.view.impl.update.UpdateContext;

import java.util.ArrayList;
Expand Down Expand Up @@ -69,9 +70,13 @@ public FusedMapActions(ViewToEntityMapper keyViewToEntityMapper, List<? extends
if (removedValue == null) {
added.put(key, value);
} else {
removed.put(new RemoveWrapper(key), removedValue);
replaces.put(key, value);
removeValueCount--;
if (removedValue != value || value instanceof DirtyTracker && ((DirtyTracker) value).$$_isDirty() && added.get(key) != value) {
removed.put(new RemoveWrapper(key), removedValue);
replaces.put(key, value);
if (removedValue != value) {
removeValueCount--;
}
}
}
} else {
removeCount--;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,24 +315,32 @@ protected boolean isIndexed() {
}

@Override
protected void addElementFlushActions(UpdateContext context, TypeDescriptor typeDescriptor, List<CollectionAction<?>> actions, V current) {
protected void addFlatViewElementFlushActions(UpdateContext context, TypeDescriptor typeDescriptor, List<CollectionAction<?>> actions, V current) {
final ViewToEntityMapper mapper = typeDescriptor.getViewToEntityMapper();
OUTER: for (int i = 0; i < current.size(); i++) {
for (int i = 0; i < current.size(); i++) {
Object o = current.get(i);
if (o instanceof MutableStateTrackable) {
MutableStateTrackable element = (MutableStateTrackable) o;
@SuppressWarnings("unchecked")
DirtyAttributeFlusher<?, E, V> flusher = (DirtyAttributeFlusher<?, E, V>) (DirtyAttributeFlusher) mapper.getNestedDirtyFlusher(context, element, (DirtyAttributeFlusher) null);
if (flusher != null) {
// Skip the element flusher if the element was already added before
EntryState state = EntryState.EXISTED;
for (CollectionAction<?> action : actions) {
if (action.getAddedObjects().contains(element)) {
continue OUTER;
if (identityContains(action.getRemovedObjects(), element)) {
state = state.onRemove();
if (identityContains(action.getAddedObjects(), element)) {
state = state.onAdd();
}
} else if (identityContains(action.getAddedObjects(), element)) {
state = state.onAdd();
}
}

// Using last = false is intentional to actually get a proper update instead of a delete and insert
actions.add(new ListSetAction<>(i, false, element, null));
// If the element was added, we don't have to introduce a set action for flushing it
if (state != EntryState.ADDED) {
// Using last = false is intentional to actually get a proper update instead of a delete and insert
actions.add(new ListSetAction<>(i, false, element, null));
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
Expand Down Expand Up @@ -196,46 +195,37 @@ protected boolean isIndexed() {
}

@Override
protected void addElementFlushActions(UpdateContext context, TypeDescriptor typeDescriptor, List<MapAction<?>> actions, V current) {
protected void addFlatViewElementFlushActions(UpdateContext context, TypeDescriptor typeDescriptor, List<MapAction<?>> actions, V current) {
final ViewToEntityMapper mapper = typeDescriptor.getViewToEntityMapper();
OUTER: for (Map.Entry<?, ?> entry : current.entrySet()) {
for (Map.Entry<?, ?> entry : current.entrySet()) {
Object o = entry.getValue();
if (o instanceof MutableStateTrackable) {
MutableStateTrackable element = (MutableStateTrackable) o;
@SuppressWarnings("unchecked")
DirtyAttributeFlusher<?, E, V> flusher = (DirtyAttributeFlusher<?, E, V>) (DirtyAttributeFlusher) mapper.getNestedDirtyFlusher(context, element, (DirtyAttributeFlusher) null);
if (flusher != null) {
// Skip the element flusher if the key was already added before
boolean wasElementAdded = false;
EntryState state = EntryState.EXISTED;
for (MapAction<?> action : actions) {
if (action.getAddedKeys().contains(entry.getKey())) {
continue OUTER;
}
if (identityContains(action.getAddedElements(), element)) {
wasElementAdded = true;
if (identityContains(action.getRemovedElements(), element)) {
state = state.onRemove();
if (identityContains(action.getAddedElements(), element)) {
state = state.onAdd();
}
} else if (identityContains(action.getAddedElements(), element)) {
state = state.onAdd();
}
}

if (wasElementAdded) {
actions.add(new MapPutAction(entry.getKey(), element, Collections.emptyMap()));
} else {
// If the element was added, we don't have to introduce a put action for flushing it
if (state != EntryState.ADDED) {
actions.add(new MapPutAction(entry.getKey(), element, current));
}
}
}
}
}

private boolean identityContains(Collection<Object> addedElements, MutableStateTrackable element) {
for (Object addedElement : addedElements) {
if (addedElement == element) {
return true;
}
}

return false;
}

@Override
protected void invokeCollectionAction(UpdateContext context, Object ownerView, Object view, V targetCollection, Object value, List<? extends MapAction<?>> collectionActions) {
if (mapping == null) {
Expand Down Expand Up @@ -1603,12 +1593,16 @@ protected DirtyAttributeFlusher<MapAttributeFlusher<E, V>, E, V> determineDirtyF
List<MapAction<Map<Object, Object>>> collectionActions = determineCollectionActions(context, initial, current, equalityChecker);

// If nothing changed in the collection and no changes should be flushed, we are done
if (collectionActions.size() == 0 && (!keyDescriptor.shouldFlushMutations() || keyDescriptor.supportsDirtyCheck()) && (!elementDescriptor.shouldFlushMutations() || elementDescriptor.supportsDirtyCheck())) {
// Always reset the actions as that indicates changes
if (collectionActions.size() == 0) {
List<CollectionElementAttributeFlusher<E, V>> elementFlushers = getElementFlushers(context, current, collectionActions);
if (current instanceof RecordingMap<?, ?, ?>) {
((RecordingMap<?, ?, ?>) current).resetActions(context);
}
return null;
// A "null" element flusher list is given when a fetch and compare is more appropriate
if (elementFlushers == null) {
return this;
}
return getReplayAndElementFlusher(context, initial, current, collectionActions, elementFlushers);
}

if (collectionActions.size() > current.size()) {
Expand All @@ -1632,13 +1626,13 @@ protected DirtyAttributeFlusher<MapAttributeFlusher<E, V>, E, V> determineDirtyF
// If we determine possible collection actions, we try to apply them, but if not
if (elementDescriptor.shouldFlushMutations()) {
List<CollectionElementAttributeFlusher<E, V>> elementFlushers = getElementFlushers(context, current, collectionActions);
if (current instanceof RecordingMap<?, ?, ?>) {
((RecordingMap<?, ?, ?>) current).resetActions(context);
}
// A "null" element flusher list is given when a fetch and compare is more appropriate
if (elementFlushers == null) {
return this;
}
if (current instanceof RecordingMap<?, ?, ?>) {
((RecordingMap<?, ?, ?>) current).resetActions(context);
}
return getReplayAndElementFlusher(context, initial, current, collectionActions, elementFlushers);
} else {
if (current instanceof RecordingMap<?, ?, ?>) {
Expand Down

0 comments on commit 13e0be4

Please sign in to comment.