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);
}
}
}
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;
}
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;
}
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;
}
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));
}
Aggregations