use of com.blazebit.persistence.view.spi.type.DirtyTracker 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);
}
}
}
}
}
use of com.blazebit.persistence.view.spi.type.DirtyTracker 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.DirtyTracker in project blaze-persistence by Blazebit.
the class DirtyStateViewAttributeAccessor method setValue.
@Override
public void setValue(Object view, Object value) {
super.setValue(view, value);
if (view instanceof MutableStateTrackable) {
MutableStateTrackable mutableStateTrackable = (MutableStateTrackable) view;
if (updatableOnly && value instanceof MutableStateTrackable) {
((MutableStateTrackable) value).$$_addReadOnlyParent(mutableStateTrackable, dirtyStateIndex);
} else if (value instanceof DirtyTracker) {
((DirtyTracker) value).$$_setParent(mutableStateTrackable, dirtyStateIndex);
}
mutableStateTrackable.$$_getMutableState()[dirtyStateIndex] = value;
if (view instanceof DirtyStateTrackable) {
if (userType != null) {
((DirtyStateTrackable) view).$$_getInitialState()[dirtyStateIndex] = userType.deepClone(value);
} else {
((DirtyStateTrackable) view).$$_getInitialState()[dirtyStateIndex] = value;
}
}
}
}
use of com.blazebit.persistence.view.spi.type.DirtyTracker in project blaze-persistence by Blazebit.
the class ViewMapper method map.
public T map(S source, Map<String, Object> optionalParameters) {
Object[] tuple = new Object[objectMappers.length];
for (int i = 0; i < objectMappers.length; i++) {
if (objectMappers[i] != null) {
tuple[i] = objectMappers[i].getValue(source, optionalParameters);
}
}
T result = objectInstantiator.newInstance(tuple);
boolean copiedInitialState = false;
if (dirtyMapping != null && source instanceof DirtyTracker) {
DirtyTracker oldDirtyTracker = (DirtyTracker) source;
DirtyTracker dirtyTracker = (DirtyTracker) result;
if (tryCopyInitialState && oldDirtyTracker instanceof DirtyStateTrackable && dirtyTracker instanceof DirtyStateTrackable) {
Object[] oldInitial = ((DirtyStateTrackable) oldDirtyTracker).$$_getInitialState();
Object[] newInitial = ((DirtyStateTrackable) dirtyTracker).$$_getInitialState();
for (int i = 0; i < dirtyMapping.length; i++) {
int dirtyStateIndex = dirtyMapping[i];
if (oldDirtyTracker.$$_isDirty(dirtyStateIndex)) {
newInitial[i] = oldInitial[dirtyStateIndex];
dirtyTracker.$$_markDirty(i);
}
}
copiedInitialState = true;
} else {
for (int i = 0; i < dirtyMapping.length; i++) {
if (oldDirtyTracker.$$_isDirty(dirtyMapping[i])) {
dirtyTracker.$$_markDirty(i);
}
}
}
}
boolean tryResetInitialState = false;
if (source instanceof EntityViewProxy) {
// CHECKSTYLE:OFF: FallThrough
switch(entityViewKindMapping) {
case MAP_REFERENCE_AND_NEW:
if (((EntityViewProxy) source).$$_isNew()) {
((MutableStateTrackable) result).$$_setIsNew(tryResetInitialState = true);
}
case MAP_REFERENCE:
if (((EntityViewProxy) source).$$_isReference()) {
((EntityViewProxy) result).$$_setIsReference(tryResetInitialState = true);
}
break;
case MARK_NEW:
((MutableStateTrackable) result).$$_setIsNew(tryResetInitialState = true);
break;
default:
throw new IllegalArgumentException("Unsupported entity view kind mapping: " + entityViewKindMapping);
}
// CHECKSTYLE:ON: FallThrough
} else if (entityViewKindMapping == EntityViewKindMapping.MARK_NEW) {
((MutableStateTrackable) result).$$_setIsNew(tryResetInitialState = true);
}
if (!copiedInitialState && tryResetInitialState && result instanceof DirtyStateTrackable) {
// Reset the initial state i.e. mark it as new
Object[] initialState = ((DirtyStateTrackable) result).$$_getInitialState();
Arrays.fill(initialState, null);
}
if (postConvert != null) {
try {
if (postConvertUsesSource) {
postConvert.invoke(result, source);
} else {
postConvert.invoke(result);
}
} catch (Exception ex) {
throw new RuntimeException("Error during invocation of post convert method!", ex);
}
}
return result;
}
Aggregations