use of com.blazebit.persistence.view.spi.type.MutableStateTrackable in project blaze-persistence by Blazebit.
the class IndexedListAttributeFlusher method addFlatViewElementFlushActions.
@Override
protected void addFlatViewElementFlushActions(UpdateContext context, TypeDescriptor typeDescriptor, List<CollectionAction<?>> actions, V current) {
final ViewToEntityMapper mapper = typeDescriptor.getViewToEntityMapper();
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) {
// At this point, we have to check the collection actions to determine if the view was added through actions somehow
// We will register a special ListSetAction if the view was not added through actions to issue an UPDATE statement
// By default, since the view is dirty, we start with the state UPDATED and go through state transitions
// based on the containment of the view in the added/removed objects collections of the actions
EntryState state = EntryState.UPDATED;
Object replacedObject = element;
for (CollectionAction<?> action : actions) {
Collection<Object> removedObjects = action.getRemovedObjects();
if (identityContains(removedObjects, element)) {
if (identityContains(action.getAddedObjects(), element)) {
// This is a ListSetAction where the old and new object are the same instances
replacedObject = element;
state = EntryState.UPDATED;
} else {
state = state.onRemove();
}
} else if (identityContains(action.getAddedObjects(), element)) {
if (removedObjects.isEmpty()) {
state = state.onAdd();
} else {
// This is a ListSetAction which has only a single element, so this is safe
replacedObject = removedObjects.iterator().next();
state = EntryState.UPDATED;
}
}
}
// and there is no action that would flush the object changes already
if (state == EntryState.UPDATED && replacedObject == element) {
// Using last = false is intentional to actually get a proper update instead of a delete and insert
actions.add(new ListSetAction<>(i, false, element, element));
}
}
}
}
}
use of com.blazebit.persistence.view.spi.type.MutableStateTrackable 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.MutableStateTrackable in project blaze-persistence by Blazebit.
the class MapAttributeFlusher method addFlatViewElementFlushActions.
@Override
protected void addFlatViewElementFlushActions(UpdateContext context, TypeDescriptor typeDescriptor, List<MapAction<?>> actions, V current) {
final ViewToEntityMapper mapper = typeDescriptor.getViewToEntityMapper();
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) {
// At this point, we have to check the collection actions to determine if the element was added through actions somehow
// We will register a special MapPutAction if the element was not added through actions to issue an UPDATE statement
// By default, since the element is dirty, we start with the state UPDATED and go through state transitions
// based on the containment of the element in the added/removed objects collections of the actions
EntryState state = EntryState.UPDATED;
Object replacedObject = element;
for (MapAction<?> action : actions) {
Collection<Object> removedElements = action.getRemovedElements();
Collection<Object> addedElements = action.getAddedElements();
if (removedElements.isEmpty()) {
if (identityContains(addedElements, element)) {
state = state.onAdd();
}
} else if (addedElements.isEmpty()) {
if (identityContains(removedElements, element)) {
state = state.onRemove();
}
} else {
// Here we have a MapPutAction or MapPutAllAction where added/removed have the same cardinality
Iterator<Object> addedIter = addedElements.iterator();
Iterator<Object> removedIter = removedElements.iterator();
while (addedIter.hasNext()) {
Object added = addedIter.next();
Object removed = removedIter.next();
if (removed == element) {
if (added == element) {
// This is a MapPutAction or MapPutAllAction where the old and new object are the same instances
replacedObject = element;
state = EntryState.UPDATED;
} else {
state = state.onRemove();
}
break;
} else if (added == element) {
replacedObject = removed;
state = EntryState.UPDATED;
break;
}
}
}
}
// and there is no action that would flush the object changes already
if (state == EntryState.UPDATED && replacedObject == element) {
actions.add(new MapPutAction<>(entry.getKey(), element, element));
}
}
}
}
}
use of com.blazebit.persistence.view.spi.type.MutableStateTrackable 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.MutableStateTrackable in project blaze-persistence by Blazebit.
the class CompositeAttributeFlusher method remove.
@Override
public List<PostFlushDeleter> remove(UpdateContext context, Object entity, Object view, Object value) {
EntityViewProxy entityView = (EntityViewProxy) value;
if (entityView instanceof MutableStateTrackable && !entityView.$$_isReference()) {
MutableStateTrackable updatableProxy = (MutableStateTrackable) entityView;
// 3. Aren't new i.e. only existing objects, no need to delete object that hasn't been persisted yet
if (persistable && context.addRemovedObject(entityView) && !updatableProxy.$$_isNew()) {
if (context.invokePreRemove(entityView)) {
Object[] state = updatableProxy.$$_getMutableState();
List<PostFlushDeleter> postFlushDeleters = new ArrayList<>();
for (int i = 0; i < state.length; i++) {
final DirtyAttributeFlusher<?, Object, Object> flusher = flushers[i];
if (flusher != null && !flusher.requiresDeleteCascadeAfterRemove()) {
postFlushDeleters.addAll(flusher.remove(context, entity, entityView, state[i]));
}
}
remove(context, entity, updatableProxy, updatableProxy, updatableProxy.$$_getId(), updatableProxy.$$_getVersion(), false);
for (PostFlushDeleter postFlushDeleter : postFlushDeleters) {
postFlushDeleter.execute(context);
}
for (int i = 0; i < state.length; i++) {
final DirtyAttributeFlusher<?, Object, Object> flusher = flushers[i];
if (flusher != null && flusher.requiresDeleteCascadeAfterRemove()) {
flusher.remove(context, entity, entityView, state[i]);
}
}
} else {
context.removeRemovedObject(entityView);
context.getEntityViewManager().update(context, entityView);
}
}
} else {
if (context.addRemovedObject(entityView)) {
if (context.invokePreRemove(entityView)) {
remove(context, entity, entityView, entityView, entityView.$$_getId(), entityView.$$_getVersion(), true);
} else {
context.removeRemovedObject(entityView);
context.getEntityViewManager().update(context, entityView);
}
}
}
return Collections.emptyList();
}
Aggregations