Skip to content

Commit

Permalink
[Bug #248] Handle cascade refresh/detach w.r.t. lazy loading proxies.
Browse files Browse the repository at this point in the history
  • Loading branch information
ledsoft committed Jun 17, 2024
1 parent 6e2869c commit 22312fd
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,17 @@ private void commitUnitOfWork() {
postCommit();
}

void removeLazyLoadingProxies(Object entity) {
assert entity != null;
final EntityType<?> et = entityType(entity.getClass());
for (FieldSpecification<?, ?> fs : et.getFieldSpecifications()) {
final Object value = EntityPropertiesUtils.getFieldValue(fs.getJavaField(), entity);
if (value instanceof LazyLoadingProxy<?> lazyLoadingProxy) {
EntityPropertiesUtils.setFieldValue(fs.getJavaField(), entity, lazyLoadingProxy.unwrap());
}
}
}

/**
* If there are any changes, commit them to the ontology.
*/
Expand Down Expand Up @@ -667,8 +678,9 @@ public <T> void refreshObject(T object) {
uowChangeSet.cancelObjectChanges(getOriginal(object));
T original = connection.find(params);
if (original == null) {
throw new EntityNotFoundException("Entity " + object + " no longer exists in the repository.");
throw new EntityNotFoundException("Entity " + stringify(object) + " no longer exists in the repository.");
}
removeLazyLoadingProxies(object);
T source = (T) cloneBuilder.buildClone(original, CloneConfiguration.withDescriptor(descriptor));
final ObjectChangeSet chSet = ChangeSetFactory.createObjectChangeSet(source, object, descriptor);
changeCalculator.calculateChanges(chSet);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
import cz.cvut.kbss.jopa.exceptions.OWLEntityExistsException;
import cz.cvut.kbss.jopa.model.descriptors.Descriptor;
import cz.cvut.kbss.jopa.model.lifecycle.LifecycleEvent;
import cz.cvut.kbss.jopa.model.metamodel.EntityType;
import cz.cvut.kbss.jopa.model.metamodel.FieldSpecification;
import cz.cvut.kbss.jopa.model.metamodel.IdentifiableEntityType;
import cz.cvut.kbss.jopa.proxy.lazy.LazyLoadingProxy;
import cz.cvut.kbss.jopa.sessions.change.ChangeSetFactory;
import cz.cvut.kbss.jopa.sessions.change.ObjectChangeSet;
import cz.cvut.kbss.jopa.sessions.validator.AttributeModificationValidator;
Expand All @@ -27,17 +25,6 @@ void detachAllManagedInstances() {
cloneMapping.forEach(this::removeLazyLoadingProxies);
}

private void removeLazyLoadingProxies(Object entity) {
assert entity != null;
final EntityType<?> et = entityType(entity.getClass());
for (FieldSpecification<?, ?> fs : et.getFieldSpecifications()) {
final Object value = EntityPropertiesUtils.getFieldValue(fs.getJavaField(), entity);
if (value instanceof LazyLoadingProxy<?> lazyLoadingProxy) {
EntityPropertiesUtils.setFieldValue(fs.getJavaField(), entity, lazyLoadingProxy.unwrap());
}
}
}

@Override
void commitToStorage() {
calculateChanges();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,9 @@

import cz.cvut.kbss.jopa.model.metamodel.FieldSpecification;
import cz.cvut.kbss.jopa.model.metamodel.Identifier;
import cz.cvut.kbss.jopa.proxy.lazy.LazyLoadingProxy;
import cz.cvut.kbss.jopa.proxy.lazy.gen.LazyLoadingEntityProxy;
import cz.cvut.kbss.jopa.sessions.MetamodelProvider;
import cz.cvut.kbss.jopa.utils.EntityPropertiesUtils;
import cz.cvut.kbss.jopa.utils.JOPALazyUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -141,7 +140,7 @@ private boolean calculateChangesInternal(ObjectChangeSet changeSet) {
}
Object clVal = EntityPropertiesUtils.getFieldValue(fs.getJavaField(), clone);
Object origVal = EntityPropertiesUtils.getFieldValue(fs.getJavaField(), original);
if (shouldSkipLazyLoadedField(origVal, clVal)) {
if (JOPALazyUtils.isLazyLoadingProxy(clVal)) {
continue;
}
boolean changed = valueChanged(origVal, clVal);
Expand All @@ -152,9 +151,4 @@ private boolean calculateChangesInternal(ObjectChangeSet changeSet) {
}
return changesFound;
}

private static boolean shouldSkipLazyLoadedField(Object originalValue, Object cloneValue) {
return (cloneValue instanceof LazyLoadingEntityProxy<?> && originalValue == null)
|| (cloneValue instanceof LazyLoadingProxy<?> && !ChangeDetectors.isNonEmptyCollection(originalValue));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,7 @@ public static void initOWLClassJMocks(IdentifiableEntityType<OWLClassJ> etMock,
when(setAMock.getBindableJavaType()).thenReturn(OWLClassA.class);
when(setAMock.getConstraints()).thenReturn(new ParticipationConstraint[]{});
when(setAMock.getDeclaringType()).thenReturn(etMock);
when(etMock.getFieldSpecification(OWLClassJ.getOwlClassAField().getName())).thenReturn(setAMock);
when(etMock.getIdentifier()).thenReturn(idMock);
when(idMock.getJavaField()).thenReturn(OWLClassJ.class.getDeclaredField("uri"));
when(idMock.getDeclaringType()).thenReturn(etMock);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import cz.cvut.kbss.jopa.model.annotations.OWLObjectProperty;
import cz.cvut.kbss.jopa.model.descriptors.Descriptor;
import cz.cvut.kbss.jopa.model.descriptors.EntityDescriptor;
import cz.cvut.kbss.jopa.model.descriptors.ObjectPropertyCollectionDescriptor;
import cz.cvut.kbss.jopa.model.metamodel.Attribute;
import cz.cvut.kbss.jopa.model.metamodel.EntityLifecycleListenerManager;
import cz.cvut.kbss.jopa.model.metamodel.IdentifiableEntityType;
Expand All @@ -43,6 +44,8 @@
import cz.cvut.kbss.jopa.sessions.ServerSession;
import cz.cvut.kbss.jopa.sessions.ServerSessionStub;
import cz.cvut.kbss.jopa.sessions.UnitOfWork;
import cz.cvut.kbss.jopa.sessions.descriptor.LoadStateDescriptorFactory;
import cz.cvut.kbss.jopa.sessions.util.LoadingParameters;
import cz.cvut.kbss.jopa.transactions.EntityTransaction;
import cz.cvut.kbss.jopa.utils.Configuration;
import org.junit.jupiter.api.BeforeEach;
Expand Down Expand Up @@ -520,7 +523,7 @@ void removeIsAbleToBreakCascadingCycle() throws Exception {
void isLoadedReturnsTrueForEagerlyLoadedAttributeOfManagedInstance() throws Exception {
final OWLClassA a = Generators.generateOwlClassAInstance();
doAnswer((invocationOnMock) -> a).when(uow)
.readObject(eq(OWLClassA.class), eq(a.getUri()), any(Descriptor.class));
.readObject(eq(OWLClassA.class), eq(a.getUri()), any(Descriptor.class));
doReturn(LoadState.LOADED).when(uow).isLoaded(a, OWLClassA.getStrAttField().getName());
final OWLClassA found = em.find(OWLClassA.class, a.getUri());
assertTrue(em.isLoaded(found, OWLClassA.getStrAttField().getName()));
Expand All @@ -539,7 +542,7 @@ void isLoadedReturnsTrueForNonNullLazilyLoadedAttribute() throws Exception {
inst.setUri(Generators.createIndividualIdentifier());
inst.setOwlClassE(new OWLClassE());
doAnswer((invocationOnMock) -> inst).when(uow)
.readObject(eq(OWLClassK.class), eq(inst.getUri()), any(Descriptor.class));
.readObject(eq(OWLClassK.class), eq(inst.getUri()), any(Descriptor.class));
doReturn(LoadState.LOADED).when(uow).isLoaded(inst, OWLClassK.getOwlClassEField().getName());
final OWLClassK found = em.find(OWLClassK.class, inst.getUri());
assertTrue(em.isLoaded(found, OWLClassK.getOwlClassEField().getName()));
Expand Down Expand Up @@ -663,4 +666,45 @@ void entityManagerIsAutoCloseable() {
}
verify(emfMock).entityManagerClosed(any(AbstractEntityManager.class));
}

@Test
void cascadeDetachDoesNothingWithLazyLoadingProxy() {
final OWLClassJ original = new OWLClassJ(Generators.createIndividualIdentifier());
original.setOwlClassA(null);
uow.getLoadStateRegistry()
.put(original, LoadStateDescriptorFactory.createNotLoaded(original, mocks.forOwlClassJ().entityType()));
when(connectorMock.find(any(LoadingParameters.class))).thenReturn(original);
final OWLClassJ toDetach = em.find(OWLClassJ.class, original.getUri());
em.detach(toDetach);
assertNotNull(toDetach.getOwlClassA());
assertTrue(toDetach.getOwlClassA().isEmpty());
}

@Test
void cascadeRefreshLoadsTriggersLazyLoading() {
final OWLClassJ lazyOriginal = new OWLClassJ(Generators.createIndividualIdentifier());
lazyOriginal.setOwlClassA(null);
uow.getLoadStateRegistry()
.put(lazyOriginal, LoadStateDescriptorFactory.createNotLoaded(lazyOriginal, mocks.forOwlClassJ()
.entityType()));
when(connectorMock.find(new LoadingParameters<>(OWLClassJ.class, lazyOriginal.getUri(), new EntityDescriptor()))).thenReturn(lazyOriginal);
final OWLClassJ loadedOriginal = new OWLClassJ(lazyOriginal.getUri());
final OWLClassA a = Generators.generateOwlClassAInstance();
loadedOriginal.setOwlClassA(Collections.singleton(a));
uow.getLoadStateRegistry()
.put(loadedOriginal, LoadStateDescriptorFactory.createAllLoaded(lazyOriginal, mocks.forOwlClassJ()
.entityType()));
final LoadingParameters<OWLClassJ> refreshLoadParams = new LoadingParameters<>(OWLClassJ.class, lazyOriginal.getUri(), new EntityDescriptor(), true);
refreshLoadParams.bypassCache();
when(connectorMock.find(refreshLoadParams)).thenReturn(loadedOriginal);
final LoadingParameters<OWLClassA> refreshALoadParams = new LoadingParameters<>(OWLClassA.class, a.getUri(), new ObjectPropertyCollectionDescriptor(mocks.forOwlClassJ()
.setAttribute()), true);
refreshALoadParams.bypassCache();
when(connectorMock.find(refreshALoadParams)).thenReturn(a);

final OWLClassJ toRefresh = em.find(OWLClassJ.class, lazyOriginal.getUri());
em.refresh(toRefresh);
assertNotNull(toRefresh.getOwlClassA());
assertEquals(1, toRefresh.getOwlClassA().size());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -794,7 +794,7 @@ void refreshThrowsEntityNotFoundForNonExistentEntity() {
when(storageMock.find(loadingParams)).thenReturn(null);

final EntityNotFoundException result = assertThrows(EntityNotFoundException.class, () -> uow.refreshObject(d));
assertThat(result.getMessage(), containsString(d + " no longer exists in the repository"));
assertThat(result.getMessage(), containsString(" no longer exists in the repository"));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,9 @@ private void initOWLClassFAxioms(OWLClassF instance) throws Exception {
doReturn(aSetAxioms).when(connectionMock).find(setDesc);
}

/**
* Bug #248
*/
@Test
void cascadeMergeOnLazyLoadingProxyDoesNothing() throws Exception {
final OWLClassO owner = new OWLClassO(Generators.generateUri());
Expand Down

0 comments on commit 22312fd

Please sign in to comment.