use of com.blazebit.persistence.view.impl.collection.RecordingMap 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.impl.collection.RecordingMap in project blaze-persistence by Blazebit.
the class MapAttributeFlusher method mergeAndRequeue.
private boolean mergeAndRequeue(UpdateContext context, RecordingMap recordingCollection, Map<Object, Object> newCollection) {
EntityManager em = context.getEntityManager();
Map<Object, Object> queuedElements = null;
final ViewToEntityMapper keyMapper = mapper.getKeyMapper();
final ViewToEntityMapper valueMapper = mapper.getValueMapper();
final boolean flushKey = keyDescriptor.shouldJpaPersistOrMerge();
final boolean flushValue = elementDescriptor.shouldJpaPersistOrMerge();
final Iterator<Map.Entry<Object, Object>> iter = getRecordingIterator(newCollection);
try {
while (iter.hasNext()) {
Map.Entry<Object, Object> entry = iter.next();
Object key = entry.getKey();
Object value = entry.getValue();
if (flushKey) {
key = persistOrMergeKey(context, em, key);
} else if (keyMapper != null) {
keyMapper.applyToEntity(context, null, key);
}
if (flushValue) {
value = persistOrMerge(em, value);
} else if (valueMapper != null) {
valueMapper.applyToEntity(context, null, value);
}
if (key != entry.getKey()) {
if (queuedElements == null) {
queuedElements = new HashMap<>(newCollection.size());
}
iter.remove();
queuedElements.put(key, value);
if (recordingCollection != null) {
recordingCollection.replaceActionElement(entry.getKey(), entry.getValue(), key, value);
}
} else if (value != entry.getValue()) {
entry.setValue(value);
}
}
} finally {
resetRecordingIterator(newCollection);
}
if (queuedElements != null) {
newCollection.putAll(queuedElements);
}
return true;
}
use of com.blazebit.persistence.view.impl.collection.RecordingMap in project blaze-persistence by Blazebit.
the class MapAttributeFlusher method mergeCollectionElements.
@Override
protected boolean mergeCollectionElements(UpdateContext context, Object ownerView, Object view, E entity, V value) {
if (elementFlushers != null) {
if (flushStrategy == FlushStrategy.ENTITY || context.isForceEntity()) {
for (CollectionElementAttributeFlusher<E, V> elementFlusher : elementFlushers) {
elementFlusher.flushEntity(context, entity, ownerView, view, value, null);
}
} else {
for (CollectionElementAttributeFlusher<E, V> elementFlusher : elementFlushers) {
elementFlusher.flushQuery(context, null, null, null, ownerView, view, value, null, null);
}
}
return !elementFlushers.isEmpty();
} else {
// Invocations of JPA merge can change the identity that leads to requeuing into the collection being required
final boolean needsRequeuing = keyDescriptor.shouldJpaMerge() || value instanceof RecordingMap<?, ?, ?> && elementDescriptor.shouldJpaMerge();
if (needsRequeuing) {
if (value instanceof RecordingMap<?, ?, ?>) {
return mergeAndRequeue(context, (RecordingMap) value, ((RecordingMap) value).getDelegate());
} else {
return mergeAndRequeue(context, null, (Map<Object, Object>) (Map<?, ?>) value);
}
} else {
@SuppressWarnings("unchecked") final EntityManager em = context.getEntityManager();
final ViewToEntityMapper keyMapper = mapper.getKeyMapper();
final ViewToEntityMapper valueMapper = mapper.getValueMapper();
final boolean flushKey = keyDescriptor.shouldJpaPersistOrMerge();
final boolean flushValue = elementDescriptor.shouldJpaPersistOrMerge();
final Iterator<Map.Entry<Object, Object>> iter = getRecordingIterator(value);
try {
while (iter.hasNext()) {
Map.Entry<Object, Object> entry = iter.next();
Object k = entry.getKey();
Object v = entry.getValue();
if (flushKey) {
persistOrMergeKey(context, em, k);
} else if (keyMapper != null) {
keyMapper.applyToEntity(context, null, k);
}
if (v != null) {
if (flushValue) {
v = persistOrMerge(em, v);
} else if (valueMapper != null) {
valueMapper.applyToEntity(context, null, v);
}
}
if (v != entry.getValue()) {
entry.setValue(v);
}
}
} finally {
resetRecordingIterator(value);
}
return true;
}
}
}
use of com.blazebit.persistence.view.impl.collection.RecordingMap in project blaze-persistence by Blazebit.
the class MapAttributeFlusher method flushQuery.
@Override
public Query flushQuery(UpdateContext context, String parameterPrefix, UpdateQueryFactory queryFactory, Query query, Object ownerView, Object view, V current, UnmappedOwnerAwareDeleter ownerAwareDeleter, DirtyAttributeFlusher<?, ?, ?> ownerFlusher) {
if (!supportsQueryFlush()) {
throw new UnsupportedOperationException("Query flush not supported for configuration!");
}
if (flushOperation != null) {
if (current instanceof RecordingMap<?, ?, ?>) {
((RecordingMap<?, ?, ?>) current).resetActions(context);
} else {
List<MapAction<Map<?, ?>>> actions = new ArrayList<>();
actions.add(new MapClearAction());
if (current != null && !current.isEmpty()) {
actions.add(new MapPutAllAction(current, Collections.emptyMap()));
}
current = replaceWithRecordingCollection(context, view, current, actions);
}
invokeFlushOperation(context, ownerView, view, null, current);
} else {
boolean isRecording = current instanceof RecordingMap<?, ?, ?>;
if (isRecording) {
RecordingMap<Map<?, ?>, ?, ?> recordingMap = (RecordingMap<Map<?, ?>, ?, ?>) current;
if (entityAttributeAccessor == null) {
// We have a correlation mapping here
recordingMap.resetActions(context);
}
Map<Object, Object> embeddables = null;
if (elementDescriptor.shouldFlushMutations()) {
if (elementDescriptor.shouldJpaPersistOrMerge()) {
mergeAndRequeue(context, recordingMap, (Map<Object, Object>) recordingMap.getDelegate());
} else if (elementDescriptor.isSubview() && (elementDescriptor.isIdentifiable() || isIndexed())) {
embeddables = flushCollectionViewElements(context, current);
}
}
if (entityAttributeAccessor != null && collectionUpdatable) {
V initial = (V) viewAttributeAccessor.getInitialValue(view);
if (initial instanceof RecordingMap<?, ?, ?>) {
initial = (V) ((RecordingMap) initial).getInitialVersion();
}
recordingMap.resetActions(context);
// If the initial object was null like it happens during full flushing, we can only replace the collection
flushCollectionOperations(context, ownerView, view, initial, current, embeddables, (FusedMapActions) null, initial != null);
}
} else {
EqualityChecker equalityChecker;
if (elementDescriptor.isSubview()) {
equalityChecker = EqualsEqualityChecker.INSTANCE;
} else {
equalityChecker = new IdentityEqualityChecker(elementDescriptor.getBasicUserType());
}
V initial = (V) viewAttributeAccessor.getInitialValue(view);
if (initial instanceof RecordingMap<?, ?, ?>) {
initial = (V) ((RecordingMap) initial).getInitialVersion();
}
List<MapAction<Map<Object, Object>>> actions;
if (initial == null && replaceWithReferenceContents || !elementDescriptor.supportsDeepEqualityCheck() || elementDescriptor.getBasicUserType() != null && !elementDescriptor.getBasicUserType().supportsDeepCloning()) {
actions = replaceActions(current);
} else {
actions = determineCollectionActions(context, initial, current, equalityChecker);
}
current = replaceWithRecordingCollection(context, view, current, actions);
Map<Object, Object> embeddables = null;
if (elementDescriptor.shouldFlushMutations()) {
if (elementDescriptor.shouldJpaPersistOrMerge()) {
mergeAndRequeue(context, null, (Map<Object, Object>) current);
} else if (elementDescriptor.isSubview() && (elementDescriptor.isIdentifiable() || isIndexed())) {
embeddables = flushCollectionViewElements(context, current);
}
}
if (entityAttributeAccessor != null && collectionUpdatable) {
// If the initial object was null like it happens during full flushing, we can only replace the collection
flushCollectionOperations(context, ownerView, view, initial, current, embeddables, (FusedMapActions) null, initial != null);
}
}
}
return query;
}
use of com.blazebit.persistence.view.impl.collection.RecordingMap 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);
}
}
}
Aggregations