Skip to content

Method: lambda$detachAllManagedInstances$0(Object)

1: package cz.cvut.kbss.jopa.sessions;
2:
3: import cz.cvut.kbss.jopa.exceptions.OWLEntityExistsException;
4: import cz.cvut.kbss.jopa.model.LoadState;
5: import cz.cvut.kbss.jopa.model.Manageable;
6: import cz.cvut.kbss.jopa.model.descriptors.Descriptor;
7: import cz.cvut.kbss.jopa.model.metamodel.EntityType;
8: import cz.cvut.kbss.jopa.model.metamodel.FieldSpecification;
9: import cz.cvut.kbss.jopa.model.metamodel.IdentifiableEntityType;
10: import cz.cvut.kbss.jopa.proxy.IndirectWrapper;
11: import cz.cvut.kbss.jopa.proxy.lazy.LazyLoadingProxy;
12: import cz.cvut.kbss.jopa.sessions.change.ChangeRecord;
13: import cz.cvut.kbss.jopa.sessions.change.ChangeSetFactory;
14: import cz.cvut.kbss.jopa.sessions.change.ObjectChangeSet;
15: import cz.cvut.kbss.jopa.sessions.descriptor.LoadStateDescriptor;
16: import cz.cvut.kbss.jopa.sessions.validator.AttributeModificationValidator;
17: import cz.cvut.kbss.jopa.utils.Configuration;
18: import cz.cvut.kbss.jopa.utils.EntityPropertiesUtils;
19:
20: import java.lang.reflect.Field;
21: import java.net.URI;
22:
23: public class ChangeTrackingUnitOfWork extends AbstractUnitOfWork {
24:
25: public ChangeTrackingUnitOfWork(AbstractSession parent, Configuration configuration) {
26: super(parent, configuration);
27: }
28:
29: @Override
30: protected <T> T readObjectInternal(Class<T> cls, Object identifier, Descriptor descriptor) {
31: final T clone = super.readObjectInternal(cls, identifier, descriptor);
32: if (clone != null) {
33: checkForIndirectObjects(clone);
34: }
35: return clone;
36: }
37:
38: /**
39: * Check if the specified entity contains a collection. If so, replace it with its indirect representation so that
40: * changes in that collection can be tracked.
41: *
42: * @param entity The entity to check
43: */
44: private void checkForIndirectObjects(Object entity) {
45: assert entity != null;
46: final EntityType<?> et = entityType(entity.getClass());
47: for (FieldSpecification<?, ?> fieldSpec : et.getFieldSpecifications()) {
48: setIndirectObjectIfPresent(entity, fieldSpec.getJavaField());
49: }
50: }
51:
52: /**
53: * Create and set indirect collection on the specified entity field.
54: * <p>
55: * If the specified field is of Collection type, and it is not already an indirect collection, create new one and
56: * set it as the value of the specified field on the specified entity.
57: *
58: * @param entity The entity collection will be set on
59: * @param field The field to set
60: * @throws IllegalArgumentException Reflection
61: */
62: private void setIndirectObjectIfPresent(Object entity, Field field) {
63: assert entity != null;
64: assert field != null;
65:
66: final Object value = EntityPropertiesUtils.getFieldValue(field, entity);
67: if (value instanceof IndirectWrapper) {
68: return;
69: }
70: if (IndirectWrapperHelper.requiresIndirectWrapper(value)) {
71: EntityPropertiesUtils.setFieldValue(field, entity, indirectWrapperHelper.createIndirectWrapper(value, entity, field));
72: }
73: }
74:
75: /**
76: * Creates an indirect collection, which wraps the specified collection instance and propagates changes to the
77: * persistence context.
78: *
79: * @param collection Collection to be proxied
80: * @param owner Collection owner instance
81: * @param field Field filled with the collection
82: * @return Indirect collection
83: */
84: public Object createIndirectCollection(Object collection, Object owner, Field field) {
85: return indirectWrapperHelper.createIndirectWrapper(collection, owner, field);
86: }
87:
88: /**
89: * If there are any changes, commit them to the ontology.
90: */
91: void commitToStorage() {
92: if (this.hasNew || this.hasChanges || this.hasDeleted) {
93: persistNewObjects();
94: calculateChanges();
95: }
96: validateIntegrityConstraints();
97: storage.commit();
98: }
99:
100: @Override
101: protected void detachAllManagedInstances() {
102: cloneMapping.forEach(instance -> {
103: removeIndirectWrappersAndProxies(instance);
104: deregisterEntityFromPersistenceContext(instance);
105: });
106: newObjectsCloneToOriginal.keySet().forEach(this::removeIndirectWrappersAndProxies);
107: }
108:
109: /**
110: * Removes {@link IndirectWrapper} and {@link LazyLoadingProxy} instances from the specified entity (if present).
111: *
112: * @param entity The entity to remove indirect wrappers from
113: */
114: private void removeIndirectWrappersAndProxies(Object entity) {
115: assert entity != null;
116: final EntityType<?> et = entityType(entity.getClass());
117: for (FieldSpecification<?, ?> fs : et.getFieldSpecifications()) {
118: final Object value = EntityPropertiesUtils.getFieldValue(fs.getJavaField(), entity);
119: if (value instanceof IndirectWrapper indirectWrapper) {
120: EntityPropertiesUtils.setFieldValue(fs.getJavaField(), entity, indirectWrapper.unwrap());
121: } else if (value instanceof LazyLoadingProxy lazyLoadingProxy) {
122: EntityPropertiesUtils.setFieldValue(fs.getJavaField(), entity, lazyLoadingProxy.unwrap());
123: }
124: }
125: }
126:
127: @Override
128: void registerClone(Object clone, Object original, Descriptor descriptor) {
129: super.registerClone(clone, original, descriptor);
130: attachPersistenceContextToEntity(clone);
131: }
132:
133: private void attachPersistenceContextToEntity(Object entity) {
134: if (isFlushingChanges()) {
135: return;
136: }
137: assert entity instanceof Manageable;
138: ((Manageable) entity).setPersistenceContext(this);
139: }
140:
141: private static void deregisterEntityFromPersistenceContext(Object entity) {
142: if (!(entity instanceof Manageable)) {
143: return;
144: }
145: ((Manageable) entity).setPersistenceContext(null);
146: }
147:
148: @Override
149: public void attributeChanged(Object entity, Field f) {
150: final IdentifiableEntityType<Object> et = entityType((Class<Object>) entity.getClass());
151: final FieldSpecification<Object, ?> fieldSpec = et.getFieldSpecification(f.getName());
152: attributeChanged(entity, fieldSpec);
153: }
154:
155: @Override
156: public void attributeChanged(Object entity, FieldSpecification<?, ?> fieldSpec) {
157: if (!isInTransaction()) {
158: throw new IllegalStateException("This unit of work is not in a transaction.");
159: }
160: final Descriptor descriptor = getDescriptor(entity);
161: final IdentifiableEntityType<Object> et = entityType((Class<Object>) entity.getClass());
162: final Object original = getOriginal(entity);
163: if (fieldSpec.isInferred() && original != null) {
164: inferredAttributeChangeValidator.validateChange(entity, getOriginal(entity), (FieldSpecification<? super Object, ?>) fieldSpec, descriptor);
165: }
166: et.getLifecycleListenerManager().invokePreUpdateCallbacks(entity);
167: storage.merge(entity, (FieldSpecification<? super Object, ?>) fieldSpec, descriptor);
168: createAndRegisterChangeRecord(entity, fieldSpec, descriptor);
169: setHasChanges();
170: setIndirectObjectIfPresent(entity, fieldSpec.getJavaField());
171: et.getLifecycleListenerManager().invokePostUpdateCallbacks(entity);
172: ((LoadStateDescriptor) loadStateRegistry.get(entity)).setLoaded(fieldSpec, LoadState.LOADED);
173: }
174:
175: private void createAndRegisterChangeRecord(Object clone, FieldSpecification<?, ?> fieldSpec,
176: Descriptor descriptor) {
177: final Object orig = getOriginal(clone);
178: if (orig == null) {
179: return;
180: }
181: final ChangeRecord record = new ChangeRecord(fieldSpec, EntityPropertiesUtils.getFieldValue(fieldSpec.getJavaField(), clone));
182: preventCachingIfReferenceIsNotLoaded(record);
183: registerChangeRecord(clone, orig, descriptor, record);
184: }
185:
186: private void registerChangeRecord(Object clone, Object orig, Descriptor descriptor, ChangeRecord record) {
187: ObjectChangeSet chSet = uowChangeSet.getExistingObjectChanges(orig);
188: if (chSet == null) {
189: chSet = ChangeSetFactory.createObjectChangeSet(orig, clone, descriptor);
190: uowChangeSet.addObjectChangeSet(chSet);
191: }
192: chSet.addChangeRecord(record);
193: }
194:
195: <T> T mergeDetachedInternal(T entity, Descriptor descriptor) {
196: assert entity != null;
197: final IdentifiableEntityType<T> et = (IdentifiableEntityType<T>) entityType(entity.getClass());
198: final URI idUri = EntityPropertiesUtils.getIdentifier(entity, et);
199:
200: final T clone = getInstanceForMerge(idUri, et, descriptor);
201: try {
202: ObjectChangeSet chSet = ChangeSetFactory.createObjectChangeSet(clone, entity, descriptor);
203: // Merge only the changed attributes
204: changeCalculator.calculateChanges(chSet);
205: // Have to check for inferred attribute changes before the actual merge
206: chSet = processInferredValueChanges(chSet);
207: if (chSet.hasChanges()) {
208: et.getLifecycleListenerManager().invokePreUpdateCallbacks(clone);
209: final DetachedInstanceMerger merger = new DetachedInstanceMerger(this);
210: merger.mergeChangesFromDetachedToManagedInstance(chSet, descriptor);
211: for (ChangeRecord record : chSet.getChanges()) {
212: AttributeModificationValidator.verifyCanModify(record.getAttribute());
213: preventCachingIfReferenceIsNotLoaded(record);
214: storage.merge(clone, (FieldSpecification<? super T, ?>) record.getAttribute(), descriptor);
215: }
216: et.getLifecycleListenerManager().invokePostUpdateCallbacks(clone);
217: registerMergeChangeSet(chSet, clone, descriptor);
218: }
219: } catch (OWLEntityExistsException e) {
220: unregisterObject(clone);
221: throw e;
222: }
223: evictAfterMerge(et, idUri, descriptor);
224: setHasChanges();
225: checkForIndirectObjects(clone);
226: return et.getJavaType().cast(clone);
227: }
228:
229: private <T> void registerMergeChangeSet(ObjectChangeSet mergeChangeSet, T clone, Descriptor descriptor) {
230: final Object original = getOriginal(clone);
231: if (uowChangeSet.getExistingObjectChanges(original) != null) {
232: final ObjectChangeSet existingChSet = uowChangeSet.getExistingObjectChanges(original);
233: mergeChangeSet.getChanges().forEach(existingChSet::addChangeRecord);
234: } else {
235: uowChangeSet.addObjectChangeSet(copyChangeSet(mergeChangeSet, original, clone, descriptor));
236: }
237: }
238:
239: @Override
240: public void registerNewObject(Object entity, Descriptor descriptor) {
241: super.registerNewObject(entity, descriptor);
242: checkForIndirectObjects(entity);
243: }
244:
245: @Override
246: public void unregisterObject(Object object) {
247: super.unregisterObject(object);
248: removeIndirectWrappersAndProxies(object);
249: deregisterEntityFromPersistenceContext(object);
250: }
251:
252: @Override
253: public void removeObject(Object entity) {
254: assert entity != null;
255: ensureManaged(entity);
256:
257: final IdentifiableEntityType<?> et = entityType(entity.getClass());
258: et.getLifecycleListenerManager().invokePreRemoveCallbacks(entity);
259: final Object identifier = getIdentifier(entity);
260: // Get the descriptor before clone is removed
261: final Descriptor descriptor = getDescriptor(entity);
262:
263: markCloneForDeletion(entity, identifier);
264: storage.remove(identifier, et.getJavaType(), descriptor);
265: et.getLifecycleListenerManager().invokePostRemoveCallbacks(entity);
266: }
267: }