diff --git a/CHANGELOG.md b/CHANGELOG.md
index 963da1365..8544ee402 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
# JOPA - Change Log
+## 2.0.1 - 2024-06-17
+- Proper implementation of `EntityManager.getReference` after 2.0.0 rewrite (Enhancement #233).
+- Log JOPA version and build date on persistence unit startup (Enhancement #243).
+- Prevent `AssertionError` on `EntityManager.flush` calls (Bug #240).
+- Fix incorrect SOQL to SPARQL translation when traversing reference and using identifier (Bug #234).
+- Fix issues with interaction of lazy loading with cascading (Bug #248).
+- Dependency updates: RDF4J 4.3.12.
+
## 2.0.0 - 2024-05-27
- Move internal API from `jopa-api` to the `jopa-impl` module (Enhancement #146).
- Modify name resolution in OWL2Java, support prefixes so that terms are better disambiguated without appending the useless `_A` suffix if possible (Enhancement #85).
diff --git a/datatype/pom.xml b/datatype/pom.xml
index 4c6a3e404..1135ee16b 100644
--- a/datatype/pom.xml
+++ b/datatype/pom.xml
@@ -6,7 +6,7 @@
jopa-all
cz.cvut.kbss.jopa
- 2.0.0
+ 2.0.1
../pom.xml
diff --git a/jopa-api/pom.xml b/jopa-api/pom.xml
index 2712eccc8..3c5eb5c92 100644
--- a/jopa-api/pom.xml
+++ b/jopa-api/pom.xml
@@ -6,7 +6,7 @@
cz.cvut.kbss.jopa
jopa-all
- 2.0.0
+ 2.0.1
../pom.xml
diff --git a/jopa-api/src/main/java/cz/cvut/kbss/jopa/exceptions/EntityNotFoundException.java b/jopa-api/src/main/java/cz/cvut/kbss/jopa/exceptions/EntityNotFoundException.java
index a4829be6f..60f81abfe 100644
--- a/jopa-api/src/main/java/cz/cvut/kbss/jopa/exceptions/EntityNotFoundException.java
+++ b/jopa-api/src/main/java/cz/cvut/kbss/jopa/exceptions/EntityNotFoundException.java
@@ -20,9 +20,12 @@
import cz.cvut.kbss.jopa.model.EntityManager;
/**
- * Thrown when {@link EntityManager#refresh(Object)} is called and the object no longer exists in the database.
+ * Thrown by the persistence provider when an entity reference obtained by
+ * {@link EntityManager#getReference(Class, Object)} is accessed but the entity does not exist. Thrown when
+ * {@link EntityManager#refresh} is called and the object no longer exists in the database.
*
- * The current transaction, if one is active and the persistence context has been joined to it, will be marked for rollback.
+ * The current transaction, if one is active and the persistence context has been joined to it, will be marked for
+ * rollback.
*/
public class EntityNotFoundException extends OWLPersistenceException {
diff --git a/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/metamodel/IdentifierVisitor.java b/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/metamodel/IdentifierVisitor.java
index 4cfdeb141..a00d41d31 100644
--- a/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/metamodel/IdentifierVisitor.java
+++ b/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/metamodel/IdentifierVisitor.java
@@ -17,6 +17,7 @@
*/
package cz.cvut.kbss.jopa.model.metamodel;
+@FunctionalInterface
public interface IdentifierVisitor {
void visit(IRIIdentifier i);
diff --git a/jopa-distribution/pom.xml b/jopa-distribution/pom.xml
index 511994397..8cc8bfbd5 100644
--- a/jopa-distribution/pom.xml
+++ b/jopa-distribution/pom.xml
@@ -6,7 +6,7 @@
cz.cvut.kbss.jopa
jopa-all
- 2.0.0
+ 2.0.1
../pom.xml
diff --git a/jopa-impl/pom.xml b/jopa-impl/pom.xml
index df77d5467..c33d18f88 100644
--- a/jopa-impl/pom.xml
+++ b/jopa-impl/pom.xml
@@ -6,7 +6,7 @@
cz.cvut.kbss.jopa
jopa-all
- 2.0.0
+ 2.0.1
../pom.xml
@@ -49,6 +49,12 @@
+
+
+ src/main/resources
+ true
+
+
org.antlr
@@ -63,6 +69,11 @@
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+ ${maven.resources.plugin.version}
+
org.apache.maven.plugins
maven-surefire-plugin
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/loaders/DefaultClasspathScanner.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/loaders/DefaultClasspathScanner.java
index f0be858e2..01a6b0099 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/loaders/DefaultClasspathScanner.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/loaders/DefaultClasspathScanner.java
@@ -88,9 +88,15 @@ protected void processElements(Enumeration urls, String scanPath) throws IO
continue;
}
visited.add(elemUri);
- LOG.trace("Processing classpath element {}", url);
+ LOG.trace("Processing classpath element {}", elemUri);
if (isJar(elemUri)) {
- processJarFile(createJarFile(url));
+ final JarFile jarFile = createJarFile(url);
+ if (!elemUri.equals(jarFile.getName()) && visited.contains(jarFile.getName())) {
+ LOG.trace("Classpath element {} maps to a JAR file {} and it has already been processed.", elemUri, jarFile.getName());
+ } else {
+ visited.add(jarFile.getName());
+ processJarFile(jarFile);
+ }
} else {
processDirectory(new File(URI.create(elemUri).getPath()), scanPath);
}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/EntityManagerImpl.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/EntityManagerImpl.java
index ceb0aa1ab..3b7f0bd5d 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/EntityManagerImpl.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/EntityManagerImpl.java
@@ -36,6 +36,7 @@
import cz.cvut.kbss.jopa.utils.CollectionFactory;
import cz.cvut.kbss.jopa.utils.Configuration;
import cz.cvut.kbss.jopa.utils.EntityPropertiesUtils;
+import cz.cvut.kbss.jopa.utils.JOPALazyUtils;
import cz.cvut.kbss.jopa.utils.Wrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -227,7 +228,7 @@ protected void exploreCascaded(Attribute, ?> at, Object merged, Object toMerge
private void mergeX(Attribute, ?> at, Object merged, Object toMerge, Descriptor descriptor) {
Object attVal = EntityPropertiesUtils.getAttributeValue(at, toMerge);
- if (attVal == null) {
+ if (attVal == null || JOPALazyUtils.isLazyLoadingProxy(attVal)) {
return;
}
if (at.isCollection()) {
@@ -261,7 +262,7 @@ public void remove(Object object) {
registerProcessedInstance(object);
// Intentional fall-through
case REMOVED:
- new SimpleOneLevelCascadeExplorer(this::remove).start(this, object, CascadeType.REMOVE);
+ new OneLevelRemoveCascadeExplorer(this::remove).start(this, object, CascadeType.REMOVE);
break;
default:
throw new IllegalArgumentException("Entity " + object + " is not managed and cannot be removed.");
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/JOPAPersistenceProvider.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/JOPAPersistenceProvider.java
index e335afedc..b46874722 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/JOPAPersistenceProvider.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/JOPAPersistenceProvider.java
@@ -17,12 +17,27 @@
*/
package cz.cvut.kbss.jopa.model;
-import java.util.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.Set;
public class JOPAPersistenceProvider implements PersistenceProvider, ProviderUtil {
+ private static final Logger LOG = LoggerFactory.getLogger(JOPAPersistenceProvider.class);
+
private static final Set EMFS = Collections.synchronizedSet(new HashSet<>());
+ public JOPAPersistenceProvider() {
+ logVersionInfo();
+ }
+
@Override
public EntityManagerFactoryImpl createEntityManagerFactory(String emName, Map properties) {
final EntityManagerFactoryImpl emf = new EntityManagerFactoryImpl(properties, this::emfClosed);
@@ -30,6 +45,18 @@ public EntityManagerFactoryImpl createEntityManagerFactory(String emName, Map, Class>> lazyLoadingProxyClasses = new ConcurrentHashMap<>();
+ // Proxy classes for results of EntityManager.getReference
+ private final Map, Class>> referenceProxyClasses = new ConcurrentHashMap<>();
private NamedQueryManager namedQueryManager;
private ResultSetMappingManager resultSetMappingManager;
@@ -151,10 +154,12 @@ public Set> getInferredClasses() {
return Collections.unmodifiableSet(inferredClasses);
}
+ @Override
public NamedQueryManager getNamedQueryManager() {
return namedQueryManager;
}
+ @Override
public ResultSetMappingManager getResultSetMappingManager() {
return resultSetMappingManager;
}
@@ -222,7 +227,7 @@ public Set> getReferringTypes(Class> cls) {
/**
* Gets a {@link cz.cvut.kbss.jopa.proxy.lazy.LazyLoadingProxy} type for the specified class.
*
- * @param cls Class to get lazy loading proxy for
+ * @param cls Class to get lazy loading proxy for, should be an entity class
* @param Type to proxy
* @return Lazy loading proxy class
*/
@@ -230,4 +235,16 @@ public Class extends X> getLazyLoadingProxy(Class cls) {
assert isEntityType(cls);
return (Class extends X>) lazyLoadingProxyClasses.computeIfAbsent(cls, c -> new LazyLoadingEntityProxyGenerator().generate(c));
}
+
+ /**
+ * Gets a {@link cz.cvut.kbss.jopa.proxy.reference.EntityReferenceProxy} type for the specified class.
+ *
+ * @param cls Class to get reference proxy for, should be an entity class
+ * @param Type to proxy
+ * @return Entity proxy class
+ */
+ public Class extends X> getEntityReferenceProxy(Class cls) {
+ assert isEntityType(cls);
+ return (Class extends X>) referenceProxyClasses.computeIfAbsent(cls, c -> new EntityReferenceProxyGenerator().generate(cls));
+ }
}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/OneLevelRemoveCascadeExplorer.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/OneLevelRemoveCascadeExplorer.java
new file mode 100644
index 000000000..409fac4f9
--- /dev/null
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/OneLevelRemoveCascadeExplorer.java
@@ -0,0 +1,37 @@
+package cz.cvut.kbss.jopa.model;
+
+import cz.cvut.kbss.jopa.model.metamodel.Attribute;
+import cz.cvut.kbss.jopa.proxy.lazy.LazyLoadingProxy;
+import cz.cvut.kbss.jopa.utils.EntityPropertiesUtils;
+import cz.cvut.kbss.jopa.utils.JOPALazyUtils;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.function.Consumer;
+
+public class OneLevelRemoveCascadeExplorer extends OneLevelCascadeExplorer {
+
+ private final Consumer removeOperation;
+
+ public OneLevelRemoveCascadeExplorer(Consumer removeOperation) {
+ this.removeOperation = removeOperation;
+ }
+
+ @Override
+ protected void exploreCascaded(Attribute, ?> at, Object o) {
+ Object attVal = EntityPropertiesUtils.getAttributeValue(at, o);
+ if (attVal == null) {
+ return;
+ }
+ if (JOPALazyUtils.isLazyLoadingProxy(attVal)) {
+ attVal = ((LazyLoadingProxy>) attVal).triggerLazyLoading();
+ }
+ if (at.isCollection()) {
+ for (final Object ox2 : new HashSet<>((Collection>) attVal)) {
+ removeOperation.accept(ox2);
+ }
+ } else {
+ removeOperation.accept(attVal);
+ }
+ }
+}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/metamodel/AbstractIdentifiableType.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/metamodel/AbstractIdentifiableType.java
index 4c5ca9da3..0e5edd1ff 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/metamodel/AbstractIdentifiableType.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/metamodel/AbstractIdentifiableType.java
@@ -28,8 +28,8 @@
import java.util.stream.Collectors;
/**
- * Instances of the type AbstractIdentifiableType represent entity or mapped superclass
- * types which can be queried for attributes, subtypes and so on.
+ * Instances of the type AbstractIdentifiableType represent entity or mapped superclass types which can be queried for
+ * attributes, subtypes and so on.
*
* @param Entity type being represented by this instance
*/
@@ -70,8 +70,8 @@ void addDeclaredQueryAttribute(final String name, final AbstractQueryAttribute
* The purpose of this method is mainly to return the generated subclass of {@link #getJavaType()} that is used for
* instantiation.
+ *
* @return Instantiable Java type
*/
public Class extends X> getInstantiableJavaType() {
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/metamodel/IdentifiableEntityType.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/metamodel/IdentifiableEntityType.java
index 066fbe16b..b15b040c9 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/metamodel/IdentifiableEntityType.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/metamodel/IdentifiableEntityType.java
@@ -21,10 +21,9 @@
import cz.cvut.kbss.jopa.model.annotations.InheritanceType;
/**
- * Instances of the type IdentifiableEntityType represent entity
- * types which can be saved to and read from storage.
+ * Instances of this type represent entity classes that can be saved to and read from storage.
*
- * @param Entity type being represented by this instance
+ * @param Entity class being represented by this instance
*/
public abstract class IdentifiableEntityType extends AbstractIdentifiableType implements EntityType {
@@ -69,9 +68,10 @@ public IRI getIRI() {
/**
* Gets inheritance type of this entity type.
*
- * If the entity type is a root if an inheritance hierarchy, the type can be defined using the {@link
- * cz.cvut.kbss.jopa.model.annotations.Inheritance} annotation. If the entity is deeper in inheritance hierarchy, it
- * is inherited from the supertype. Otherwise, it defaults to {@link cz.cvut.kbss.jopa.utils.Constants#DEFAULT_INHERITANCE_TYPE}.
+ * If the entity type is a root if an inheritance hierarchy, the type can be defined using the
+ * {@link cz.cvut.kbss.jopa.model.annotations.Inheritance} annotation. If the entity is deeper in inheritance
+ * hierarchy, it is inherited from the supertype. Otherwise, it defaults to
+ * {@link cz.cvut.kbss.jopa.utils.Constants#DEFAULT_INHERITANCE_TYPE}.
*
* @return Inheritance strategy for this entity type
*/
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/metamodel/RDFCollectionAttribute.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/metamodel/RDFCollectionAttribute.java
index 521837230..2ef042c6d 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/metamodel/RDFCollectionAttribute.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/metamodel/RDFCollectionAttribute.java
@@ -84,6 +84,7 @@ public RDFCollectionAttributeBuilder converter(ConverterWrapper converter)
return this;
}
+ @Override
public RDFCollectionAttribute build() {
return new RDFCollectionAttribute<>(this);
}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/metamodel/gen/ManageableClassGenerator.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/metamodel/gen/ManageableClassGenerator.java
index d53b428ba..cad1aba0a 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/metamodel/gen/ManageableClassGenerator.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/metamodel/gen/ManageableClassGenerator.java
@@ -87,7 +87,11 @@ private void outputGeneratedClass(DynamicType.Unloaded extends T> typeDef)
public static class SetterInterceptor {
- public static void set(@This Manageable instance, @Origin Method setter) throws Exception {
+ private SetterInterceptor() {
+ throw new AssertionError();
+ }
+
+ public static void set(@This Manageable instance, @Origin Method setter) {
final UnitOfWork pc = instance.getPersistenceContext();
if (pc == null || !pc.isInTransaction()) {
return;
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/DefaultInstanceLoader.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/DefaultInstanceLoader.java
index 4c32d0bf1..442506e28 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/DefaultInstanceLoader.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/DefaultInstanceLoader.java
@@ -35,12 +35,6 @@ T loadEntity(LoadingParameters loadingParameters) {
return loadInstance(loadingParameters, et);
}
- @Override
- T loadReference(LoadingParameters loadingParameters) {
- final IdentifiableEntityType et = metamodel.entity(loadingParameters.getEntityClass());
- return loadReferenceInstance(loadingParameters, et);
- }
-
static DefaultInstanceLoaderBuilder builder() {
return new DefaultInstanceLoaderBuilder();
}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/EntityConstructor.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/EntityConstructor.java
index 4f818c24c..33ae36e65 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/EntityConstructor.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/EntityConstructor.java
@@ -203,6 +203,8 @@ private void populateQueryAttributes(T instance, EntityType et, LoadState
if (queryAttribute.getFetchType() != FetchType.LAZY) {
populateQueryAttribute(instance, queryAttribute, queryFactory, et);
loadStateDescriptor.setLoaded(queryAttribute, LoadState.LOADED);
+ } else {
+ loadStateDescriptor.setLoaded(queryAttribute, LoadState.NOT_LOADED);
}
}
}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/EntityInstanceLoader.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/EntityInstanceLoader.java
index 588d8fce4..296617524 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/EntityInstanceLoader.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/EntityInstanceLoader.java
@@ -19,7 +19,6 @@
import cz.cvut.kbss.jopa.datatype.util.Pair;
import cz.cvut.kbss.jopa.exceptions.StorageAccessException;
-import cz.cvut.kbss.jopa.sessions.cache.CacheManager;
import cz.cvut.kbss.jopa.model.MetamodelImpl;
import cz.cvut.kbss.jopa.model.descriptors.Descriptor;
import cz.cvut.kbss.jopa.model.metamodel.Attribute;
@@ -27,6 +26,7 @@
import cz.cvut.kbss.jopa.model.metamodel.IdentifiableEntityType;
import cz.cvut.kbss.jopa.model.metamodel.PluralAttribute;
import cz.cvut.kbss.jopa.oom.exception.EntityReconstructionException;
+import cz.cvut.kbss.jopa.sessions.cache.CacheManager;
import cz.cvut.kbss.jopa.sessions.descriptor.LoadStateDescriptor;
import cz.cvut.kbss.jopa.sessions.descriptor.LoadStateDescriptorFactory;
import cz.cvut.kbss.jopa.sessions.util.LoadStateDescriptorRegistry;
@@ -36,7 +36,6 @@
import cz.cvut.kbss.ontodriver.descriptor.AxiomDescriptor;
import cz.cvut.kbss.ontodriver.exception.OntoDriverException;
import cz.cvut.kbss.ontodriver.model.Axiom;
-import cz.cvut.kbss.ontodriver.model.NamedResource;
import java.net.URI;
import java.util.Collection;
@@ -83,19 +82,6 @@ abstract class EntityInstanceLoader {
*/
abstract T loadEntity(LoadingParameters loadingParameters);
- /**
- * Loads entity reference.
- *
- * I.e., the object may contain only the identifier, other attributes could be loaded lazily. However, if a
- * corresponding entity is already cached, it can be returned instead.
- *
- * @param loadingParameters Reference loading parameters. Note that cache bypassing and forced eager loading
- * configuration is ignored
- * @param Entity type
- * @return Loaded entity reference, possibly {@code null}
- */
- abstract T loadReference(LoadingParameters loadingParameters);
-
U loadInstance(LoadingParameters loadingParameters, IdentifiableEntityType et) {
final URI identifier = loadingParameters.getIdentifier();
final Descriptor descriptor = loadingParameters.getDescriptor();
@@ -137,7 +123,9 @@ private LoadStateDescriptor> getLoadStatDescriptor(Object instance, EntityType
return LoadStateDescriptorFactory.createAllUnknown(instance, (EntityType) et);
}
- private void recursivelyProcessCachedEntityReferences(Object instance, EntityType> et, Map visited, List>>> handlers) {
+ private void recursivelyProcessCachedEntityReferences(Object instance, EntityType> et,
+ Map visited,
+ List>>> handlers) {
if (visited.containsKey(instance)) {
return;
}
@@ -161,20 +149,6 @@ private void recursivelyProcessCachedEntityReferences(Object instance, EntityTyp
});
}
- T loadReferenceInstance(LoadingParameters loadingParameters, IdentifiableEntityType extends T> et) {
- final URI identifier = loadingParameters.getIdentifier();
- final Axiom typeAxiom = descriptorFactory.createForReferenceLoading(identifier, et);
- try {
- final boolean contains =
- storageConnection.contains(typeAxiom, loadingParameters.getDescriptor().getContexts());
- return contains ? entityBuilder.createEntityInstance(identifier, et) : null;
- } catch (OntoDriverException e) {
- throw new StorageAccessException(e);
- } catch (cz.cvut.kbss.jopa.exception.InstantiationException e) {
- throw new EntityReconstructionException(e);
- }
- }
-
abstract static class EntityInstanceLoaderBuilder {
private Connection storageConnection;
private MetamodelImpl metamodel;
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/EntityReferenceFactory.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/EntityReferenceFactory.java
new file mode 100644
index 000000000..41007b1d1
--- /dev/null
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/EntityReferenceFactory.java
@@ -0,0 +1,50 @@
+package cz.cvut.kbss.jopa.oom;
+
+import cz.cvut.kbss.jopa.exceptions.OWLPersistenceException;
+import cz.cvut.kbss.jopa.model.MetamodelImpl;
+import cz.cvut.kbss.jopa.proxy.reference.EntityReferenceProxy;
+import cz.cvut.kbss.jopa.sessions.UnitOfWork;
+import cz.cvut.kbss.jopa.sessions.util.LoadingParameters;
+import cz.cvut.kbss.jopa.utils.EntityPropertiesUtils;
+
+import java.lang.reflect.InvocationTargetException;
+
+class EntityReferenceFactory {
+
+ private final MetamodelImpl metamodel;
+
+ private final UnitOfWork uow;
+
+ EntityReferenceFactory(MetamodelImpl metamodel, UnitOfWork uow) {
+ this.metamodel = metamodel;
+ this.uow = uow;
+ }
+
+ /**
+ * Creates entity reference proxy with initialized identifier and associated persistence context.
+ *
+ * @param loadingParameters Parameters for creating the reference
+ * @param Type of the expected instance
+ * @return Entity reference proxy
+ */
+ T createReferenceProxy(LoadingParameters loadingParameters) {
+ assert loadingParameters != null;
+
+ final Class extends T> referenceProxyClass = metamodel.getEntityReferenceProxy(loadingParameters.getEntityClass());
+ try {
+ final T reference = referenceProxyClass.getDeclaredConstructor().newInstance();
+ assert reference instanceof EntityReferenceProxy>;
+ final EntityReferenceProxy> referenceProxy = (EntityReferenceProxy>) reference;
+ referenceProxy.setIdentifier(loadingParameters.getIdentifier());
+ referenceProxy.setType((Class) loadingParameters.getEntityClass());
+ referenceProxy.setPersistenceContext(uow);
+ referenceProxy.setDescriptor(loadingParameters.getDescriptor());
+ EntityPropertiesUtils.setIdentifier(loadingParameters.getIdentifier(), reference, metamodel.entity(loadingParameters.getEntityClass()));
+ return reference;
+ } catch (InstantiationException | IllegalAccessException | NoSuchMethodException |
+ InvocationTargetException e) {
+ // We do not expect this to happen, as we generated the proxy class
+ throw new OWLPersistenceException("Unable to instantiate entity reference proxy class " + referenceProxyClass, e);
+ }
+ }
+}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/ListDescriptorFactory.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/ListDescriptorFactory.java
index 70c205a4c..e9a799230 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/ListDescriptorFactory.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/ListDescriptorFactory.java
@@ -16,6 +16,10 @@
*/
class ListDescriptorFactory {
+ private ListDescriptorFactory() {
+ throw new AssertionError();
+ }
+
static SimpleListDescriptor createSimpleListDescriptor(NamedResource owner, ListAttribute, ?> attribute) {
final boolean inferred = attribute.isInferred();
final Assertion listProperty = Assertion.createObjectPropertyAssertion(attribute.getIRI().toURI(), inferred);
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/ObjectOntologyMapper.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/ObjectOntologyMapper.java
index f7986ca22..d7cce1386 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/ObjectOntologyMapper.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/ObjectOntologyMapper.java
@@ -48,16 +48,15 @@ public interface ObjectOntologyMapper {
T loadEntity(LoadingParameters loadingParameters);
/**
- * Loads a reference to an entity corresponding to the specified parameters.
+ * Gets a reference to an entity corresponding to the specified parameters.
*
- * The reference is usually an empty object with attributes being loaded lazily. However, it may be also be
- * retrieved from the cache, in which case its attributes will be loaded.
+ * The reference is an empty object with state fetched upon first access.
*
* @param loadingParameters Reference loading parameters
* @param Entity type
- * @return Loaded entity reference or {@code null} if there is none such
+ * @return Entity reference
*/
- T loadReference(LoadingParameters loadingParameters);
+ T getReference(LoadingParameters loadingParameters);
/**
* Loads entity field value and sets it on the specified entity.
@@ -126,8 +125,8 @@ Set> getAttributeAxioms(T entity, FieldSpecification super T, ?>
* Checks if the specified attribute value of the specified entity is inferred in the repository.
*
* Note that attribute context may be ignored by the underlying repository, based on its inference storing strategy.
- * Also note that if the corresponding statement is both inferred and asserted, this method will return {@code
- * true}.
+ * Also note that if the corresponding statement is both inferred and asserted, this method will return
+ * {@code true}.
*
* @param entity Entity whose attribute value to examine
* @param fieldSpec Field specification representing the property to examine
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/ObjectOntologyMapperImpl.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/ObjectOntologyMapperImpl.java
index 83a984a5b..f54afc4ae 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/ObjectOntologyMapperImpl.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/ObjectOntologyMapperImpl.java
@@ -79,6 +79,8 @@ public class ObjectOntologyMapperImpl implements ObjectOntologyMapper, EntityMap
private final EntityInstanceLoader defaultInstanceLoader;
private final EntityInstanceLoader twoStepInstanceLoader;
+ private final EntityReferenceFactory referenceFactory;
+
public ObjectOntologyMapperImpl(AbstractUnitOfWork uow, Connection connection) {
this.uow = Objects.requireNonNull(uow);
this.storageConnection = Objects.requireNonNull(connection);
@@ -98,6 +100,7 @@ public ObjectOntologyMapperImpl(AbstractUnitOfWork uow, Connection connection) {
.descriptorFactory(descriptorFactory)
.entityBuilder(entityBuilder).cache(getCache())
.loadStateRegistry(uow.getLoadStateRegistry()).build();
+ this.referenceFactory = new EntityReferenceFactory(uow.getMetamodel(), uow);
}
private CacheManager getCache() {
@@ -146,15 +149,10 @@ private T loadEntityInternal(LoadingParameters loadingParameters) {
}
@Override
- public T loadReference(LoadingParameters loadingParameters) {
+ public T getReference(LoadingParameters loadingParameters) {
assert loadingParameters != null;
- final IdentifiableEntityType et = getEntityType(loadingParameters.getEntityClass());
- if (et.hasSubtypes()) {
- return twoStepInstanceLoader.loadReference(loadingParameters);
- } else {
- return defaultInstanceLoader.loadReference(loadingParameters);
- }
+ return referenceFactory.createReferenceProxy(loadingParameters);
}
@Override
@@ -238,7 +236,7 @@ private void persistPendingReferences(T instance, NamedResource identifier)
final ListValueDescriptor desc = list.getDescriptor();
ListPropertyStrategy.addIndividualsToDescriptor(desc, list.getValues(), et);
if (desc instanceof SimpleListValueDescriptor) {
- // TODO This can be an update or a persist
+ // This can be a persist or an update, calling update works for both
storageConnection.lists().updateSimpleList((SimpleListValueDescriptor) desc);
} else {
storageConnection.lists().updateReferencedList((ReferencedListValueDescriptor) desc);
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/TwoStepInstanceLoader.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/TwoStepInstanceLoader.java
index 7d7776892..4e0802148 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/TwoStepInstanceLoader.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/TwoStepInstanceLoader.java
@@ -17,10 +17,8 @@
*/
package cz.cvut.kbss.jopa.oom;
-import cz.cvut.kbss.jopa.exception.InstantiationException;
import cz.cvut.kbss.jopa.exceptions.StorageAccessException;
import cz.cvut.kbss.jopa.model.metamodel.IdentifiableEntityType;
-import cz.cvut.kbss.jopa.oom.exception.EntityReconstructionException;
import cz.cvut.kbss.jopa.oom.metamodel.PolymorphicEntityTypeResolver;
import cz.cvut.kbss.jopa.sessions.util.LoadingParameters;
import cz.cvut.kbss.ontodriver.exception.OntoDriverException;
@@ -50,19 +48,6 @@ T loadEntity(LoadingParameters loadingParameters) {
}
}
- @Override
- T loadReference(LoadingParameters loadingParameters) {
- final IdentifiableEntityType rootEt = metamodel.entity(loadingParameters.getEntityClass());
- try {
- final IdentifiableEntityType extends T> et = resolveEntityType(loadingParameters, rootEt);
- return et != null ? entityBuilder.createEntityInstance(loadingParameters.getIdentifier(), et) : null;
- } catch (OntoDriverException e) {
- throw new StorageAccessException(e);
- } catch (InstantiationException e) {
- throw new EntityReconstructionException(e);
- }
- }
-
private IdentifiableEntityType extends T> resolveEntityType(LoadingParameters loadingParameters,
IdentifiableEntityType rootEt) throws OntoDriverException {
NamedResource individual = NamedResource.create(loadingParameters.getIdentifier());
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/change/ChangeTrackingIndirectCollection.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/change/ChangeTrackingIndirectCollection.java
index 4edb73f6d..dc31bd38e 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/change/ChangeTrackingIndirectCollection.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/change/ChangeTrackingIndirectCollection.java
@@ -55,7 +55,7 @@ protected ChangeTrackingIndirectCollection(Object owner, Field f, UnitOfWork per
protected void persistChange() {
assert persistenceContext != null;
- if (persistenceContext.isInTransaction() && !persistenceContext.isInCommit()) {
+ if (persistenceContext.isInTransaction() && !persistenceContext.isFlushingChanges()) {
persistenceContext.attributeChanged(owner, field);
}
}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/change/ChangeTrackingIndirectMultilingualString.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/change/ChangeTrackingIndirectMultilingualString.java
index 086d907d4..3373d2390 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/change/ChangeTrackingIndirectMultilingualString.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/change/ChangeTrackingIndirectMultilingualString.java
@@ -56,7 +56,7 @@ public ChangeTrackingIndirectMultilingualString(Object owner, Field f, UnitOfWor
private void notifyPersistenceContext() {
assert persistenceContext != null;
- if (persistenceContext.isInTransaction() && !persistenceContext.isInCommit()) {
+ if (persistenceContext.isInTransaction() && !persistenceContext.isFlushingChanges()) {
persistenceContext.attributeChanged(owner, field);
}
}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/lazy/LazyLoadingCollectionProxy.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/lazy/LazyLoadingCollectionProxy.java
index 4f99b9e47..8c5cb2739 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/lazy/LazyLoadingCollectionProxy.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/lazy/LazyLoadingCollectionProxy.java
@@ -15,11 +15,11 @@
*/
abstract class LazyLoadingCollectionProxy, E> implements LazyLoadingProxy, Collection {
- protected final transient O owner;
- protected final transient FieldSpecification super O, T> fieldSpec;
- protected final transient UnitOfWork persistenceContext;
+ protected final O owner;
+ protected final FieldSpecification super O, T> fieldSpec;
+ protected final UnitOfWork persistenceContext;
- private transient T value;
+ private T value;
public LazyLoadingCollectionProxy(O owner, FieldSpecification super O, T> fieldSpec,
UnitOfWork persistenceContext) {
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/lazy/LazyLoadingMapProxy.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/lazy/LazyLoadingMapProxy.java
index d54abe8f2..a30c289a9 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/lazy/LazyLoadingMapProxy.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/lazy/LazyLoadingMapProxy.java
@@ -18,9 +18,9 @@
*/
public class LazyLoadingMapProxy implements LazyLoadingProxy>, Map {
- protected final transient T owner;
- protected final transient FieldSpecification super T, Map> fieldSpec;
- protected final transient UnitOfWork persistenceContext;
+ protected final T owner;
+ protected final FieldSpecification super T, Map> fieldSpec;
+ protected final UnitOfWork persistenceContext;
private Map value;
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/lazy/gen/LazyLoadingEntityProxyGenerator.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/lazy/gen/LazyLoadingEntityProxyGenerator.java
index d958e2a82..be6d2a1bc 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/lazy/gen/LazyLoadingEntityProxyGenerator.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/lazy/gen/LazyLoadingEntityProxyGenerator.java
@@ -72,6 +72,10 @@ public Class extends T> generate(Class entityClass) {
public static class GetterInterceptor {
+ private GetterInterceptor() {
+ throw new AssertionError();
+ }
+
@RuntimeType
public static Object get(@This LazyLoadingEntityProxy proxy, @Origin Method getter) {
final Object loaded = proxy.triggerLazyLoading();
@@ -85,6 +89,10 @@ public static Object get(@This LazyLoadingEntityProxy proxy, @Origin Meth
public static class SetterInterceptor {
+ private SetterInterceptor() {
+ throw new AssertionError();
+ }
+
public static void set(@This LazyLoadingEntityProxy proxy, @Origin Method setter,
@AllArguments Object[] args) {
final Object loaded = proxy.triggerLazyLoading();
@@ -98,6 +106,10 @@ public static void set(@This LazyLoadingEntityProxy proxy, @Origin Method
public static class ProxyMethodsInterceptor {
+ private ProxyMethodsInterceptor() {
+ throw new AssertionError();
+ }
+
public static boolean isLoaded(@This LazyLoadingEntityProxy proxy, @FieldValue("value") T value) {
return value != null;
}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/lazy/gen/PersistentPropertyGetterMatcher.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/lazy/gen/PersistentPropertyGetterMatcher.java
index 85d6ab388..4d51dd5b1 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/lazy/gen/PersistentPropertyGetterMatcher.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/lazy/gen/PersistentPropertyGetterMatcher.java
@@ -17,9 +17,9 @@
*
* @param MethodDescription
*/
-class PersistentPropertyGetterMatcher extends PersistentPropertyMatcher {
+public class PersistentPropertyGetterMatcher extends PersistentPropertyMatcher {
- PersistentPropertyGetterMatcher(Class> parentType) {
+ public PersistentPropertyGetterMatcher(Class> parentType) {
super(parentType);
}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/reference/EntityReferenceProxy.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/reference/EntityReferenceProxy.java
new file mode 100644
index 000000000..e1cf32fb3
--- /dev/null
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/reference/EntityReferenceProxy.java
@@ -0,0 +1,27 @@
+package cz.cvut.kbss.jopa.proxy.reference;
+
+import cz.cvut.kbss.jopa.exception.LazyLoadingException;
+import cz.cvut.kbss.jopa.exceptions.EntityNotFoundException;
+import cz.cvut.kbss.jopa.utils.IdentifierTransformer;
+
+public interface EntityReferenceProxy extends EntityReferenceProxyPropertyAccessor {
+
+ default T triggerLoading() {
+ if (isLoaded()) {
+ return getValue();
+ }
+ if (getPersistenceContext() == null || !getPersistenceContext().isActive()) {
+ throw new LazyLoadingException("No active persistence context is available to load reference of type " + getType() + " with identifier + " + getIdentifier());
+ }
+ final T loaded = getPersistenceContext().readObject(getType(), getIdentifier(), getDescriptor());
+ if (loaded == null) {
+ throw new EntityNotFoundException("Entity '" + getType().getSimpleName() + "' with id " + IdentifierTransformer.stringifyIri(getIdentifier()) + " not found in the repository.");
+ }
+ setValue(loaded);
+ return loaded;
+ }
+
+ default boolean isLoaded() {
+ return getValue() != null;
+ }
+}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/reference/EntityReferenceProxyGenerator.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/reference/EntityReferenceProxyGenerator.java
new file mode 100644
index 000000000..8871d0289
--- /dev/null
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/reference/EntityReferenceProxyGenerator.java
@@ -0,0 +1,115 @@
+package cz.cvut.kbss.jopa.proxy.reference;
+
+import cz.cvut.kbss.jopa.exception.LazyLoadingException;
+import cz.cvut.kbss.jopa.exceptions.AttributeModificationForbiddenException;
+import cz.cvut.kbss.jopa.model.descriptors.Descriptor;
+import cz.cvut.kbss.jopa.model.metamodel.AnnotatedAccessor;
+import cz.cvut.kbss.jopa.model.metamodel.gen.PersistenceContextAwareClassGenerator;
+import cz.cvut.kbss.jopa.model.metamodel.gen.PersistentPropertySetterMatcher;
+import cz.cvut.kbss.jopa.proxy.lazy.gen.PersistentPropertyGetterMatcher;
+import cz.cvut.kbss.jopa.sessions.UnitOfWork;
+import net.bytebuddy.ByteBuddy;
+import net.bytebuddy.NamingStrategy;
+import net.bytebuddy.description.modifier.FieldPersistence;
+import net.bytebuddy.description.modifier.Visibility;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.dynamic.DynamicType;
+import net.bytebuddy.implementation.FieldAccessor;
+import net.bytebuddy.implementation.MethodDelegation;
+import net.bytebuddy.implementation.bind.annotation.AllArguments;
+import net.bytebuddy.implementation.bind.annotation.Origin;
+import net.bytebuddy.implementation.bind.annotation.RuntimeType;
+import net.bytebuddy.implementation.bind.annotation.This;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.util.Objects;
+
+import static net.bytebuddy.matcher.ElementMatchers.isGetter;
+import static net.bytebuddy.matcher.ElementMatchers.isSetter;
+
+public class EntityReferenceProxyGenerator implements PersistenceContextAwareClassGenerator {
+
+ private static final Logger LOG = LoggerFactory.getLogger(EntityReferenceProxyGenerator.class);
+
+ private final ByteBuddy byteBuddy = new ByteBuddy().with(new NamingStrategy.AbstractBase() {
+ @Override
+ protected String name(TypeDescription typeDescription) {
+ return typeDescription.getSimpleName() + "_ReferenceProxy";
+ }
+ });
+
+ @Override
+ public Class extends T> generate(Class entityClass) {
+ Objects.requireNonNull(entityClass);
+ LOG.trace("Generating reference proxy for entity class {}.", entityClass);
+ DynamicType.Unloaded extends T> typeDef = byteBuddy.subclass(entityClass)
+ .annotateType(new GeneratedEntityReferenceProxyImpl())
+ .defineField("identifier", URI.class, Visibility.PRIVATE, FieldPersistence.TRANSIENT)
+ .defineField("type", Class.class, Visibility.PRIVATE, FieldPersistence.TRANSIENT)
+ .defineField("descriptor", Descriptor.class, Visibility.PRIVATE, FieldPersistence.TRANSIENT)
+ .defineField("persistenceContext", UnitOfWork.class, Visibility.PRIVATE, FieldPersistence.TRANSIENT)
+ // Have to use Object, because otherwise it won't generate a setter for us
+ .defineField("value", entityClass, Visibility.PRIVATE, FieldPersistence.TRANSIENT)
+ .implement(TypeDescription.Generic.Builder.parameterizedType(EntityReferenceProxyPropertyAccessor.class, entityClass)
+ .build())
+ .intercept(FieldAccessor.ofBeanProperty())
+ .implement(EntityReferenceProxy.class)
+ .method(isSetter().and(new PersistentPropertySetterMatcher<>(entityClass)))
+ .intercept(MethodDelegation.to(SetterInterceptor.class))
+ .method(isGetter().and(new PersistentPropertyGetterMatcher<>(entityClass)))
+ .intercept(MethodDelegation.to(GetterInterceptor.class))
+ .make();
+ LOG.debug("Generated dynamic type {} for entity class {}.", typeDef, entityClass);
+ return typeDef.load(getClass().getClassLoader()).getLoaded();
+ }
+
+ public static class GetterInterceptor {
+
+ private GetterInterceptor() {
+ throw new AssertionError();
+ }
+
+ @RuntimeType
+ public static Object get(@This EntityReferenceProxy proxy, @Origin Method getter) {
+ if (isIdentifierField(proxy, getter)) {
+ return proxy.getIdentifier();
+ }
+ final Object loaded = proxy.triggerLoading();
+ try {
+ return getter.invoke(loaded);
+ } catch (InvocationTargetException | IllegalAccessException e) {
+ throw new LazyLoadingException("Unable to invoke getter after loading referenced object.", e);
+ }
+ }
+ }
+
+ private static boolean isIdentifierField(EntityReferenceProxy proxy, Method accessor) {
+ final String fieldName = AnnotatedAccessor.from(accessor).getPropertyName();
+ return proxy.getPersistenceContext().getMetamodel().entity(proxy.getType()).getIdentifier().getName()
+ .equals(fieldName);
+ }
+
+ public static class SetterInterceptor {
+
+ private SetterInterceptor() {
+ throw new AssertionError();
+ }
+
+ public static void set(@This EntityReferenceProxy proxy, @Origin Method setter,
+ @AllArguments Object[] args) {
+ if (isIdentifierField(proxy, setter)) {
+ throw new AttributeModificationForbiddenException("Cannot change entity reference identifier.");
+ }
+ final Object loaded = proxy.triggerLoading();
+ try {
+ setter.invoke(loaded, args);
+ } catch (InvocationTargetException | IllegalAccessException e) {
+ throw new LazyLoadingException("Unable to invoke setter after loading referenced object.", e);
+ }
+ }
+ }
+}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/reference/EntityReferenceProxyPropertyAccessor.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/reference/EntityReferenceProxyPropertyAccessor.java
new file mode 100644
index 000000000..fc143f368
--- /dev/null
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/reference/EntityReferenceProxyPropertyAccessor.java
@@ -0,0 +1,34 @@
+package cz.cvut.kbss.jopa.proxy.reference;
+
+import cz.cvut.kbss.jopa.model.descriptors.Descriptor;
+import cz.cvut.kbss.jopa.sessions.UnitOfWork;
+
+import java.net.URI;
+
+/**
+ * Provides access to persistence context-related attributes needed by entity reference proxies.
+ *
+ * This interface should be implemented by the classes generated by {@link EntityReferenceProxyGenerator}.
+ */
+public interface EntityReferenceProxyPropertyAccessor {
+
+ URI getIdentifier();
+
+ void setIdentifier(URI identifier);
+
+ Class getType();
+
+ void setType(Class entityType);
+
+ Descriptor getDescriptor();
+
+ void setDescriptor(Descriptor descriptor);
+
+ UnitOfWork getPersistenceContext();
+
+ void setPersistenceContext(UnitOfWork uow);
+
+ T getValue();
+
+ void setValue(T value);
+}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/reference/GeneratedEntityReferenceProxy.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/reference/GeneratedEntityReferenceProxy.java
new file mode 100644
index 000000000..a534e8020
--- /dev/null
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/reference/GeneratedEntityReferenceProxy.java
@@ -0,0 +1,14 @@
+package cz.cvut.kbss.jopa.proxy.reference;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marker interface for generated entity reference proxy classes.
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface GeneratedEntityReferenceProxy {
+}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/reference/GeneratedEntityReferenceProxyImpl.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/reference/GeneratedEntityReferenceProxyImpl.java
new file mode 100644
index 000000000..02a42a777
--- /dev/null
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/reference/GeneratedEntityReferenceProxyImpl.java
@@ -0,0 +1,10 @@
+package cz.cvut.kbss.jopa.proxy.reference;
+
+import java.lang.annotation.Annotation;
+
+public class GeneratedEntityReferenceProxyImpl implements GeneratedEntityReferenceProxy {
+ @Override
+ public Class extends Annotation> annotationType() {
+ return GeneratedEntityReferenceProxy.class;
+ }
+}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/query/QueryHints.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/query/QueryHints.java
index ec9be2808..b4797e014 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/query/QueryHints.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/query/QueryHints.java
@@ -33,10 +33,10 @@ public class QueryHints {
* By target ontology, it is meant either the shared ontology, which does not contain pending transactional changes,
* or the transactional ontology (w.r.t. the persistence context issuing the query), where transactional changes may
* influence the query results.
- *
+ *
* Note that OntoDriver implementations may choose to ignore the selection depending on their internal transaction
* and query execution mechanism.
- *
+ *
* Valid values are {@literal CENTRAL} and {@literal TRANSACTIONAL}.
*/
public static final String TARGET_ONTOLOGY = "cz.cvut.kbss.jopa.query.targetOntology";
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/query/soql/AttributeNode.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/query/soql/AttributeNode.java
index ad7a03424..bffa860f0 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/query/soql/AttributeNode.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/query/soql/AttributeNode.java
@@ -66,4 +66,9 @@ public boolean requiresFilterExpression() {
public String toFilterExpression(String filterParam, String filterValue) {
return filterParam;
}
+
+ @Override
+ public String toString() {
+ return getValue() + (getChild() != null ? "." + getChild() : "");
+ }
}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/query/soql/FunctionNode.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/query/soql/FunctionNode.java
index a49d3c77d..7fcd0f879 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/query/soql/FunctionNode.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/query/soql/FunctionNode.java
@@ -79,4 +79,9 @@ public String toFilterExpression(String filterParam, String filterValue) {
return SoqlFunctionTranslator.getSparqlFunction(functionName) + "(" + child.toFilterExpression(filterParam,
filterValue) + ")";
}
+
+ @Override
+ public String toString() {
+ return functionName + "(" + (child != null ? child.toString() : "") + ")";
+ }
}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/query/soql/SoqlAttribute.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/query/soql/SoqlAttribute.java
index 0cf4f81da..87f520b41 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/query/soql/SoqlAttribute.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/query/soql/SoqlAttribute.java
@@ -169,4 +169,9 @@ private static String toIri(SoqlNode node) {
final String nodeIri = node.getIri();
return SparqlConstants.RDF_TYPE_SHORTCUT.equals(nodeIri) ? nodeIri : IdentifierTransformer.stringifyIri(nodeIri);
}
+
+ @Override
+ public String toString() {
+ return (value != null ? value + "." : "") + (getFirstNode() != null ? getFirstNode().toString() : " ");
+ }
}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/query/soql/SoqlParameter.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/query/soql/SoqlParameter.java
index 24826b64f..bc2296ce6 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/query/soql/SoqlParameter.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/query/soql/SoqlParameter.java
@@ -57,4 +57,9 @@ public String getAsValue(String rootVariable) {
}
return buildParam.toString();
}
+
+ @Override
+ public String toString() {
+ return firstNode != null ? firstNode.toString() : getClass().getSimpleName();
+ }
}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/query/soql/SoqlQueryListener.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/query/soql/SoqlQueryListener.java
index bce566df3..0d6024b1a 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/query/soql/SoqlQueryListener.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/query/soql/SoqlQueryListener.java
@@ -135,6 +135,10 @@ private void pushNewAttribute(SoqlAttribute myAttr) {
this.attrPointer = myAttr;
}
+ private void popAttribute() {
+ this.attrPointer = attributes.remove(attributes.size() - 1);
+ }
+
@Override
public void exitJoinedParams(SoqlParser.JoinedParamsContext ctx) {
}
@@ -192,15 +196,18 @@ public void enterObjWithAttr(SoqlParser.ObjWithAttrContext ctx) {
SoqlNode objectNode = new AttributeNode(owner);
SoqlNode attributeNode = new AttributeNode(objectNode, attribute);
objectNode.setChild(attributeNode);
+ setIris(objectNode);
+ SoqlAttribute newAttr = new SoqlAttribute(objectNode);
if (isIdentifier(objectNode, attributeNode)) {
this.isInObjectIdentifierExpression = true;
- if (projectedVariable.equals(objectNode.getValue())) {
+ if (projectedVariable.equals(objectNode.getValue()) && currentPointerIsNotAttributeReference()) {
attrPointer.setProjected(true);
+ } else {
+ newAttr.setProjected(true);
+ pushNewAttribute(newAttr);
}
} else {
- setIris(objectNode);
- SoqlAttribute myAttr = new SoqlAttribute(objectNode);
- pushNewAttribute(myAttr);
+ pushNewAttribute(newAttr);
}
}
@@ -216,6 +223,10 @@ private boolean isIdentifier(SoqlNode objectNode, SoqlNode attributeNode) {
return entityType.getIdentifier().getName().equals(attributeNode.getValue());
}
+ private boolean currentPointerIsNotAttributeReference() {
+ return !attrPointer.getFirstNode().hasChild();
+ }
+
@Override
public void exitObjWithAttr(SoqlParser.ObjWithAttrContext ctx) {
}
@@ -402,10 +413,16 @@ public void exitComparisonExpression(SoqlParser.ComparisonExpressionContext ctx)
if (attrPointer.isProjected()) {
this.rootVariable = SoqlUtils.soqlVariableToSparqlVariable(whereClauseValue.getText());
}
- if (attributes.size() > 1 && !attrPointer.getFirstNode().getChild().hasChild()) {
- final String varName = whereClauseValue.getText();
- attrPointer.getFirstNode().getChild().setValue(
- varName.charAt(0) == SoqlConstants.VARIABLE_PREFIX ? varName.substring(1) : varName);
+ if (attributes.size() > 1) {
+ if (isRootIdentifier(attrPointer)) {
+ // If the current attribute is identifier (i.e., something like root.iri = :iri) and there are other attributes to work with,
+ // just pop it and not use it in the query anymore
+ popAttribute();
+ } else {
+ final String varName = whereClauseValue.getText();
+ attrPointer.getFirstNode().getChild().setValue(
+ varName.charAt(0) == SoqlConstants.VARIABLE_PREFIX ? varName.substring(1) : varName);
+ }
}
} else {
attrPointer.setOperator(new ComparisonOperator(operator));
@@ -414,6 +431,11 @@ public void exitComparisonExpression(SoqlParser.ComparisonExpressionContext ctx)
this.isInObjectIdentifierExpression = false;
}
+ private boolean isRootIdentifier(SoqlAttribute attribute) {
+ return isIdentifier(attribute.getFirstNode(), attribute.getFirstNode().getChild()) &&
+ !attribute.getFirstNode().getChild().hasChild();
+ }
+
@Override
public void enterTables(SoqlParser.TablesContext ctx) {
}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/AbstractUnitOfWork.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/AbstractUnitOfWork.java
index 868c185fe..2e3fc9c1a 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/AbstractUnitOfWork.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/AbstractUnitOfWork.java
@@ -83,6 +83,7 @@ public abstract class AbstractUnitOfWork extends AbstractSession implements Unit
final Map deletedObjects;
final Map newObjectsCloneToOriginal;
final Map newObjectsKeyToClone = new HashMap<>();
+ private final Map referenceProxies;
RepositoryMap repoMap;
final LoadStateDescriptorRegistry loadStateRegistry;
@@ -93,7 +94,7 @@ public abstract class AbstractUnitOfWork extends AbstractSession implements Unit
private boolean transactionActive;
private boolean isActive;
- private boolean inCommit;
+ private boolean flushingChanges;
UnitOfWorkChangeSet uowChangeSet = ChangeSetFactory.createUoWChangeSet();
@@ -113,6 +114,7 @@ public AbstractUnitOfWork(AbstractSession parent, Configuration configuration) {
this.cloneMapping = cloneToOriginals.keySet();
this.deletedObjects = new IdentityHashMap<>();
this.newObjectsCloneToOriginal = new IdentityHashMap<>();
+ this.referenceProxies = new IdentityHashMap<>();
this.repoMap = new RepositoryMap();
this.loadStateRegistry = new LoadStateDescriptorRegistry(this::stringify);
this.indirectWrapperHelper = new IndirectWrapperHelper(this);
@@ -185,7 +187,6 @@ public void commit() {
if (!isActive()) {
throw new IllegalStateException("Cannot commit inactive Unit of Work!");
}
- this.inCommit = true;
commitUnitOfWork();
LOG.trace("UnitOfWork commit finished.");
}
@@ -194,11 +195,23 @@ public void commit() {
* Commit this Unit of Work.
*/
private void commitUnitOfWork() {
+ this.flushingChanges = true;
commitToStorage();
mergeChangesIntoParent();
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.
*/
@@ -228,7 +241,7 @@ private void evictPossiblyUpdatedReferencesFromCache() {
private void postCommit() {
final boolean changes = hasChanges();
clear();
- this.inCommit = false;
+ this.flushingChanges = false;
if (changes) {
getLiveObjectCache().evictInferredObjects();
}
@@ -307,8 +320,22 @@ private T getManagedClone(Class cls, Object identifier, Descriptor descri
@Override
public T getReference(Class cls, Object identifier, Descriptor descriptor) {
- // TODO Temporary just so that the API works
- return readObject(cls, identifier, descriptor);
+ Objects.requireNonNull(cls);
+ Objects.requireNonNull(identifier);
+ Objects.requireNonNull(descriptor);
+
+ final T managed = readManagedObject(cls, identifier, descriptor);
+ if (managed != null) {
+ return managed;
+ }
+ if (keysToClones.containsKey(identifier)) {
+ throw new EntityNotFoundException("Entity '" + cls.getSimpleName() + "' with id " + IdentifierTransformer.stringifyIri(identifier) + " not found.");
+ }
+ final T reference = storage.getReference(new LoadingParameters<>(cls, getValueAsURI(identifier), descriptor));
+ registerEntityWithOntologyContext(reference, descriptor);
+ referenceProxies.put(reference, reference);
+ loadStateRegistry.put(reference, LoadStateDescriptorFactory.createNotLoaded(reference, entityType(cls)));
+ return reference;
}
@Override
@@ -332,7 +359,7 @@ public EntityState getState(Object entity) {
return EntityState.REMOVED;
} else if (newObjectsCloneToOriginal.containsKey(entity)) {
return EntityState.MANAGED_NEW;
- } else if (cloneMapping.contains(entity)) {
+ } else if (cloneMapping.contains(entity) || referenceProxies.containsKey(entity)) {
return EntityState.MANAGED;
} else {
return EntityState.NOT_MANAGED;
@@ -348,7 +375,7 @@ public EntityState getState(Object entity, Descriptor descriptor) {
return EntityState.REMOVED;
} else if (newObjectsCloneToOriginal.containsKey(entity) && isInRepository(descriptor, entity)) {
return EntityState.MANAGED_NEW;
- } else if (cloneMapping.contains(entity) && isInRepository(descriptor, entity)) {
+ } else if ((cloneMapping.contains(entity) || referenceProxies.containsKey(entity)) && isInRepository(descriptor, entity)) {
return EntityState.MANAGED;
} else {
return EntityState.NOT_MANAGED;
@@ -364,7 +391,12 @@ public boolean isObjectNew(Object entity) {
public boolean isObjectManaged(Object entity) {
Objects.requireNonNull(entity);
- return cloneMapping.contains(entity) && !deletedObjects.containsKey(entity) || newObjectsCloneToOriginal.containsKey(entity);
+ return (cloneMapping.contains(entity) || isManagedReference(entity)) && !deletedObjects.containsKey(entity)
+ || newObjectsCloneToOriginal.containsKey(entity);
+ }
+
+ private boolean isManagedReference(Object entity) {
+ return referenceProxies.containsKey(entity);
}
@Override
@@ -420,7 +452,7 @@ public Object registerExistingObject(Object entity, CloneRegistrationDescriptor
return getCloneForOriginal(entity);
}
final CloneConfiguration cloneConfig = CloneConfiguration.withDescriptor(registrationDescriptor.getDescriptor())
- .forPersistenceContext(!isInCommit())
+ .forPersistenceContext(!isFlushingChanges())
.addPostRegisterHandlers(registrationDescriptor.getPostCloneHandlers());
Object clone = cloneBuilder.buildClone(entity, cloneConfig);
assert clone != null;
@@ -466,7 +498,11 @@ private void calculateNewObjects(UnitOfWorkChangeSet changeSet) {
private void calculateDeletedObjects(final UnitOfWorkChangeSet changeSet) {
for (Object clone : deletedObjects.keySet()) {
final Descriptor descriptor = getDescriptor(clone);
- final Object original = getOriginal(clone);
+ Object original = getOriginal(clone);
+ if (original == null) {
+ assert referenceProxies.containsKey(clone);
+ original = clone;
+ }
changeSet.addDeletedObjectChangeSet(ChangeSetFactory.createDeleteObjectChange(clone, original, descriptor));
changeSet.cancelObjectChanges(original);
}
@@ -515,7 +551,7 @@ public Object getOriginal(Object clone) {
* @param original Original to register
*/
public void registerOriginalForNewClone(Object clone, Object original) {
- assert inCommit;
+ assert flushingChanges;
assert newObjectsCloneToOriginal.containsKey(clone);
newObjectsCloneToOriginal.put(clone, original);
}
@@ -642,8 +678,9 @@ public 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);
@@ -772,8 +809,8 @@ public boolean isInTransaction() {
}
@Override
- public boolean isInCommit() {
- return inCommit;
+ public boolean isFlushingChanges() {
+ return flushingChanges;
}
@Override
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/ChangeTrackingUnitOfWork.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/ChangeTrackingUnitOfWork.java
index 8fa4216ce..fa8eac962 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/ChangeTrackingUnitOfWork.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/ChangeTrackingUnitOfWork.java
@@ -131,7 +131,7 @@ void registerClone(Object clone, Object original, Descriptor descriptor) {
}
private void attachPersistenceContextToEntity(Object entity) {
- if (isInCommit()) {
+ if (isFlushingChanges()) {
return;
}
assert entity instanceof Manageable;
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/ConnectionWrapper.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/ConnectionWrapper.java
index bbc75e061..0e5785a43 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/ConnectionWrapper.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/ConnectionWrapper.java
@@ -62,7 +62,7 @@ public T find(LoadingParameters loadingParameters) {
}
public T getReference(LoadingParameters loadingParameters) {
- return mapper.loadReference(loadingParameters);
+ return mapper.getReference(loadingParameters);
}
public void merge(T entity, FieldSpecification super T, ?> fieldSpec, Descriptor descriptor) {
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/OnCommitChangePropagatingUnitOfWork.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/OnCommitChangePropagatingUnitOfWork.java
index ca7fd9f9b..1854926e1 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/OnCommitChangePropagatingUnitOfWork.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/OnCommitChangePropagatingUnitOfWork.java
@@ -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;
@@ -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();
@@ -55,6 +42,7 @@ void commitToStorage() {
chSet.getChanges()
.forEach(record -> {
AttributeModificationValidator.verifyCanModify(record.getAttribute());
+ preventCachingIfReferenceIsNotLoaded(record);
storage.merge(entity, (FieldSpecification super Object, ?>) record.getAttribute(), chSet.getDescriptor());
});
et.getLifecycleListenerManager().invokePostUpdateCallbacks(entity);
@@ -112,12 +100,6 @@ T mergeDetachedInternal(T toMerge, Descriptor descriptor) {
return et.getJavaType().cast(clone);
}
- @Override
- public T getReference(Class cls, Object identifier, Descriptor descriptor) {
- // TODO
- return readObject(cls, identifier, descriptor);
- }
-
@Override
public void removeObject(Object entity) {
assert entity != null;
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/UnitOfWork.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/UnitOfWork.java
index bfab6fa77..87a854405 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/UnitOfWork.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/UnitOfWork.java
@@ -85,11 +85,11 @@ public interface UnitOfWork extends ConfigurationHolder, MetamodelProvider, Wrap
boolean isInTransaction();
/**
- * Returns true if this Unit of Work is currently committing changes to the repository.
+ * Returns true if this Unit of Work is currently flushing changes to the repository.
*
- * @return {@code true} if the UoW is in commit, {@code false} otherwise
+ * @return {@code true} if the UoW is flushing changes, {@code false} otherwise
*/
- boolean isInCommit();
+ boolean isFlushingChanges();
/**
* Return true if the given entity is managed.
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/change/Change.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/change/Change.java
index e66b80cc8..45b06a59e 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/change/Change.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/change/Change.java
@@ -18,6 +18,7 @@
package cz.cvut.kbss.jopa.sessions.change;
import cz.cvut.kbss.jopa.model.descriptors.Descriptor;
+import cz.cvut.kbss.jopa.utils.MetamodelUtils;
import java.net.URI;
@@ -31,12 +32,14 @@ public interface Change {
*
* @return Object type
*/
- Class> getObjectClass();
+ default Class> getObjectClass() {
+ return MetamodelUtils.getEntityClass(getClone().getClass());
+ }
/**
* Gets the clone with changes.
*
- * @return Clone
+ * @return Clone, never {@code null}
*/
Object getClone();
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/change/ChangeCalculator.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/change/ChangeCalculator.java
index eb04dcf74..533aaea39 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/change/ChangeCalculator.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/change/ChangeCalculator.java
@@ -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;
@@ -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);
@@ -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));
- }
}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/change/DeleteObjectChange.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/change/DeleteObjectChange.java
index 27569db1c..d92993fdb 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/change/DeleteObjectChange.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/change/DeleteObjectChange.java
@@ -38,11 +38,6 @@ public DeleteObjectChange(Object clone, Object original, Descriptor descriptor)
this.descriptor = Objects.requireNonNull(descriptor);
}
- @Override
- public Class> getObjectClass() {
- return clone.getClass();
- }
-
@Override
public Object getClone() {
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/change/NewObjectChange.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/change/NewObjectChange.java
index 03f27c6bc..ef125bff6 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/change/NewObjectChange.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/change/NewObjectChange.java
@@ -18,11 +18,6 @@ public NewObjectChange(Object object, Descriptor descriptor) {
this.descriptor = Objects.requireNonNull(descriptor);
}
- @Override
- public Class> getObjectClass() {
- return object.getClass();
- }
-
@Override
public Object getClone() {
return object;
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/change/ObjectChangeSet.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/change/ObjectChangeSet.java
index cfbf0f8ab..5cbf88ccd 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/change/ObjectChangeSet.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/change/ObjectChangeSet.java
@@ -71,11 +71,6 @@ public boolean hasChanges() {
return !attributesToChange.isEmpty();
}
- @Override
- public Class> getObjectClass() {
- return cloneObject.getClass();
- }
-
@Override
public Object getOriginal() {
return changedObject;
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/merge/ManagedTypeValueMerger.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/merge/ManagedTypeValueMerger.java
index decd4bde9..f352ca1c8 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/merge/ManagedTypeValueMerger.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/merge/ManagedTypeValueMerger.java
@@ -43,6 +43,9 @@ Object getValueToSet(Object mergedValue, Descriptor descriptor) {
if (mergedValue == null) {
return null;
}
+ if (uow.contains(mergedValue)) {
+ return mergedValue;
+ }
final Object identifier = EntityPropertiesUtils.getIdentifier(mergedValue, uow.getMetamodel());
if (identifier == null) {
return mergedValue;
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/util/CloneRegistrationDescriptor.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/util/CloneRegistrationDescriptor.java
index 5e66febe1..916ed8a43 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/util/CloneRegistrationDescriptor.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/sessions/util/CloneRegistrationDescriptor.java
@@ -44,8 +44,12 @@ public List> getPostCloneHandlers() {
@Override
public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof CloneRegistrationDescriptor that)) return false;
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof CloneRegistrationDescriptor that)) {
+ return false;
+ }
return isAllEager() == that.isAllEager() && Objects.equals(getDescriptor(),
that.getDescriptor()) && Objects.equals(
getPostCloneHandlers(), that.getPostCloneHandlers());
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/utils/JOPALazyUtils.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/utils/JOPALazyUtils.java
index 2dd33d2d8..4a77e4249 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/utils/JOPALazyUtils.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/utils/JOPALazyUtils.java
@@ -10,6 +10,20 @@
*/
public class JOPALazyUtils {
+ private JOPALazyUtils() {
+ throw new AssertionError();
+ }
+
+ /**
+ * Checks if this specified object is a lazy loading proxy (instance of {@link LazyLoadingProxy}).
+ *
+ * @param object Object to investigate
+ * @return {@code true} if argument is lazy loading proxy, {@code false} otherwise
+ */
+ public static boolean isLazyLoadingProxy(Object object) {
+ return object instanceof LazyLoadingProxy>;
+ }
+
/**
* Triggers loading on the specified lazy loading proxy.
*
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/utils/MetamodelUtils.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/utils/MetamodelUtils.java
index a357dbe6f..cc57936c3 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/utils/MetamodelUtils.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/utils/MetamodelUtils.java
@@ -19,6 +19,7 @@
import cz.cvut.kbss.jopa.model.metamodel.Metamodel;
import cz.cvut.kbss.jopa.model.metamodel.gen.GeneratedEntityClass;
+import cz.cvut.kbss.jopa.proxy.reference.GeneratedEntityReferenceProxy;
import java.net.URI;
import java.util.Collection;
@@ -66,6 +67,7 @@ public static void checkForModuleSignatureExtension(Collection> types, Metamod
* @return Entity class
*/
public static Class super T> getEntityClass(Class cls) {
- return cls.getAnnotation(GeneratedEntityClass.class) != null ? cls.getSuperclass() : cls;
+ return (cls.getAnnotation(GeneratedEntityClass.class) != null || cls.getAnnotation(GeneratedEntityReferenceProxy.class) != null)
+ ? cls.getSuperclass() : cls;
}
}
diff --git a/jopa-impl/src/main/resources/jopa.properties b/jopa-impl/src/main/resources/jopa.properties
new file mode 100644
index 000000000..56ce495df
--- /dev/null
+++ b/jopa-impl/src/main/resources/jopa.properties
@@ -0,0 +1,2 @@
+cz.cvut.jopa.version=${project.version}
+cz.cvut.jopa.build.timestamp=${maven.build.timestamp}
diff --git a/jopa-impl/src/test/java/cz/cvut/kbss/jopa/environment/utils/MetamodelFactory.java b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/environment/utils/MetamodelFactory.java
index 2f894b003..b569100e8 100644
--- a/jopa-impl/src/test/java/cz/cvut/kbss/jopa/environment/utils/MetamodelFactory.java
+++ b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/environment/utils/MetamodelFactory.java
@@ -496,7 +496,6 @@ public static void initOWLClassHMocks(IdentifiableEntityType etMock,
when(etMock.getInstantiableJavaType()).thenReturn((Class) instantiableTypeGenerator.generate(OWLClassH.class));
when(etMock.getPersistenceType()).thenReturn(Type.PersistenceType.ENTITY);
when(etMock.getIRI()).thenReturn(IRI.create(OWLClassH.getClassIri()));
- when(etMock.getAttribute(OWLClassH.getOwlClassAField().getName())).thenReturn(clsAMock);
when(etMock.getName()).thenReturn(OWLClassH.class.getSimpleName());
when(etMock.getAttributes()).thenReturn(
new HashSet<>(Arrays.>asList(clsAMock, clsGMock)));
@@ -525,6 +524,8 @@ public static void initOWLClassHMocks(IdentifiableEntityType etMock,
when(etMock.getFieldSpecification(clsAMock.getName())).thenReturn(clsAMock);
when(etMock.getFieldSpecification(clsGMock.getName())).thenReturn(clsGMock);
+ when(etMock.getAttribute(clsAMock.getName())).thenReturn(clsAMock);
+ when(etMock.getAttribute(clsGMock.getName())).thenReturn(clsGMock);
when(etMock.getIdentifier()).thenReturn(idMock);
when(idMock.getName()).thenReturn(OWLClassH.class.getDeclaredField("uri").getName());
when(idMock.getJavaField()).thenReturn(OWLClassH.class.getDeclaredField("uri"));
@@ -585,6 +586,7 @@ public static void initOWLClassJMocks(IdentifiableEntityType 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);
diff --git a/jopa-impl/src/test/java/cz/cvut/kbss/jopa/model/EntityManagerImplTest.java b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/model/EntityManagerImplTest.java
index 10cae1532..f056cc8cb 100644
--- a/jopa-impl/src/test/java/cz/cvut/kbss/jopa/model/EntityManagerImplTest.java
+++ b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/model/EntityManagerImplTest.java
@@ -33,16 +33,19 @@
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;
import cz.cvut.kbss.jopa.model.metamodel.Identifier;
+import cz.cvut.kbss.jopa.proxy.lazy.LazyLoadingSetProxy;
import cz.cvut.kbss.jopa.sessions.ChangeTrackingUnitOfWork;
import cz.cvut.kbss.jopa.sessions.ConnectionWrapper;
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.AbstractUnitOfWork;
+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;
@@ -59,6 +62,7 @@
import java.net.URI;
import java.util.Collections;
import java.util.HashSet;
+import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -133,6 +137,15 @@ void testCascadeMergeOnNullCollection() {
// the merged object is correctly passed to merge in UoW
}
+ @Test
+ void cascadeMergeDoesNothingForLazyLoadingProxy() {
+ final OWLClassJ j = new OWLClassJ(Generators.createIndividualIdentifier());
+ j.setOwlClassA(new LazyLoadingSetProxy<>(j, mocks.forOwlClassJ().setAttribute(), uow));
+
+ em.merge(j);
+ verify(uow).mergeDetached(j, new EntityDescriptor());
+ }
+
@Test
void mergeDetachedWithSingletonSet() {
final OWLClassJ j = new OWLClassJ();
@@ -511,7 +524,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()));
@@ -530,7 +543,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()));
@@ -654,4 +667,69 @@ 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 refreshLoadParams = new LoadingParameters<>(OWLClassJ.class, lazyOriginal.getUri(), new EntityDescriptor(), true);
+ refreshLoadParams.bypassCache();
+ when(connectorMock.find(refreshLoadParams)).thenReturn(loadedOriginal);
+ final LoadingParameters 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());
+ }
+
+ @Test
+ void cascadeRemoveTriggersLazyLoadingAndCascadesRemoval() {
+ 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 toRemove = em.find(OWLClassJ.class, lazyOriginal.getUri());
+ final OWLClassA a = Generators.generateOwlClassAInstance();
+ uow.getLoadStateRegistry()
+ .put(a, LoadStateDescriptorFactory.createAllLoaded(a, mocks.forOwlClassA().entityType()));
+ doAnswer(inv -> {
+ final OWLClassJ target = inv.getArgument(0);
+ target.setOwlClassA(new HashSet<>(Set.of(a)));
+ return null;
+ }).when(connectorMock).loadFieldValue(toRemove, mocks.forOwlClassJ().setAttribute(), new EntityDescriptor());
+
+ em.remove(toRemove);
+ verify(connectorMock).remove(toRemove.getUri(), OWLClassJ.class, new EntityDescriptor());
+ verify(connectorMock).remove(a.getUri(), OWLClassA.class, new ObjectPropertyCollectionDescriptor(mocks.forOwlClassJ()
+ .setAttribute()));
+ }
}
diff --git a/jopa-impl/src/test/java/cz/cvut/kbss/jopa/model/MetamodelImplTest.java b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/model/MetamodelImplTest.java
index e4884db89..2867b868a 100644
--- a/jopa-impl/src/test/java/cz/cvut/kbss/jopa/model/MetamodelImplTest.java
+++ b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/model/MetamodelImplTest.java
@@ -26,6 +26,7 @@
import cz.cvut.kbss.jopa.model.annotations.*;
import cz.cvut.kbss.jopa.model.metamodel.*;
import cz.cvut.kbss.jopa.proxy.lazy.gen.LazyLoadingEntityProxy;
+import cz.cvut.kbss.jopa.proxy.reference.EntityReferenceProxy;
import cz.cvut.kbss.jopa.query.NamedQueryManager;
import cz.cvut.kbss.jopa.utils.Configuration;
import cz.cvut.kbss.ontodriver.config.OntoDriverProperties;
@@ -715,6 +716,7 @@ void getLazyLoadingProxyReturnsLazyLoadingProxyClassForSpecifiedType() {
sut.build(entityClasses);
final Class extends OWLClassA> result = sut.getLazyLoadingProxy(OWLClassA.class);
assertNotNull(result);
+ assertTrue(LazyLoadingEntityProxy.class.isAssignableFrom(result));
assertThat(result, typeCompatibleWith(OWLClassA.class));
}
@@ -727,4 +729,25 @@ void getLazyLoadingProxyCachesGeneratedProxyClasses() {
final Class extends OWLClassA> resultTwo = sut.getLazyLoadingProxy(OWLClassA.class);
assertSame(resultOne, resultTwo);
}
+
+ @Test
+ void getEntityReferenceProxyReturnsGeneratedEntityReferenceProxyClassForSpecifiedType() {
+ final Set> entityClasses = new HashSet<>(List.of(OWLClassA.class));
+ final MetamodelImpl sut = new MetamodelImpl(conf);
+ sut.build(entityClasses);
+ final Class extends OWLClassA> result = sut.getEntityReferenceProxy(OWLClassA.class);
+ assertNotNull(result);
+ assertTrue(EntityReferenceProxy.class.isAssignableFrom(result));
+ assertThat(result, typeCompatibleWith(OWLClassA.class));
+ }
+
+ @Test
+ void getEntityReferenceProxyCachesGeneratedProxyClasses() {
+ final Set> entityClasses = new HashSet<>(List.of(OWLClassA.class));
+ final MetamodelImpl sut = new MetamodelImpl(conf);
+ sut.build(entityClasses);
+ final Class extends OWLClassA> resultOne = sut.getEntityReferenceProxy(OWLClassA.class);
+ final Class extends OWLClassA> resultTwo = sut.getEntityReferenceProxy(OWLClassA.class);
+ assertSame(resultOne, resultTwo);
+ }
}
diff --git a/jopa-impl/src/test/java/cz/cvut/kbss/jopa/oom/DefaultInstanceLoaderTest.java b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/oom/DefaultInstanceLoaderTest.java
index e13e7a024..92994e56d 100644
--- a/jopa-impl/src/test/java/cz/cvut/kbss/jopa/oom/DefaultInstanceLoaderTest.java
+++ b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/oom/DefaultInstanceLoaderTest.java
@@ -19,21 +19,15 @@
import cz.cvut.kbss.jopa.environment.OWLClassA;
import cz.cvut.kbss.jopa.environment.OWLClassD;
-import cz.cvut.kbss.jopa.environment.Vocabulary;
import cz.cvut.kbss.jopa.environment.utils.Generators;
import cz.cvut.kbss.jopa.environment.utils.MetamodelMocks;
import cz.cvut.kbss.jopa.exceptions.StorageAccessException;
-import cz.cvut.kbss.jopa.model.descriptors.EntityDescriptor;
import cz.cvut.kbss.jopa.model.metamodel.IdentifiableEntityType;
import cz.cvut.kbss.jopa.sessions.util.LoadStateDescriptorRegistry;
import cz.cvut.kbss.jopa.sessions.util.LoadingParameters;
import cz.cvut.kbss.ontodriver.descriptor.AxiomDescriptor;
import cz.cvut.kbss.ontodriver.exception.OntoDriverException;
-import cz.cvut.kbss.ontodriver.model.Assertion;
import cz.cvut.kbss.ontodriver.model.Axiom;
-import cz.cvut.kbss.ontodriver.model.AxiomImpl;
-import cz.cvut.kbss.ontodriver.model.NamedResource;
-import cz.cvut.kbss.ontodriver.model.Value;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -135,68 +129,6 @@ void loadEntityBypassesCacheWhenConfiguredTo() throws Exception {
verify(cacheMock, never()).contains(etAMock.getJavaType(), IDENTIFIER, descriptor);
}
- @Test
- void loadReferenceVerifiesClassAssertionExistenceAndBuildsEntityInstanceWithIdentifier() throws Exception {
- final Axiom typeAxiom =
- new AxiomImpl<>(NamedResource.create(IDENTIFIER), Assertion.createClassAssertion(false),
- new Value<>(NamedResource.create(
- Vocabulary.c_OwlClassA)));
- when(connectionMock.contains(typeAxiom, Collections.emptySet())).thenReturn(true);
- when(descriptorFactoryMock.createForReferenceLoading(IDENTIFIER, etAMock)).thenReturn(typeAxiom);
- when(entityConstructorMock.createEntityInstance(IDENTIFIER, etAMock)).thenReturn(entityA);
-
- final OWLClassA result = instanceLoader.loadReference(loadingParameters);
- assertNotNull(result);
- verify(descriptorFactoryMock).createForReferenceLoading(IDENTIFIER, etAMock);
- verify(connectionMock).contains(typeAxiom, Collections.emptySet());
- verify(entityConstructorMock).createEntityInstance(IDENTIFIER, etAMock);
- }
-
- @Test
- void loadReferenceChecksForClassAssertionInCorrectContext() throws Exception {
- final EntityDescriptor descriptor = new EntityDescriptor(Generators.createIndividualIdentifier());
- final Axiom typeAxiom =
- new AxiomImpl<>(NamedResource.create(IDENTIFIER), Assertion.createClassAssertion(false),
- new Value<>(NamedResource.create(
- Vocabulary.c_OwlClassA)));
- when(connectionMock.contains(eq(typeAxiom), any())).thenReturn(true);
- when(descriptorFactoryMock.createForReferenceLoading(IDENTIFIER, etAMock)).thenReturn(typeAxiom);
- when(entityConstructorMock.createEntityInstance(IDENTIFIER, etAMock)).thenReturn(entityA);
-
- final OWLClassA result =
- instanceLoader.loadReference(new LoadingParameters<>(OWLClassA.class, IDENTIFIER, descriptor));
- assertNotNull(result);
- verify(connectionMock).contains(typeAxiom, descriptor.getContexts());
- }
-
- @Test
- void loadReferenceReturnsNullWhenStorageDoesNotContainTypeAssertionAxiom() throws Exception {
- final Axiom typeAxiom =
- new AxiomImpl<>(NamedResource.create(IDENTIFIER), Assertion.createClassAssertion(false),
- new Value<>(NamedResource.create(
- Vocabulary.c_OwlClassA)));
- when(connectionMock.contains(any(), any())).thenReturn(false);
- when(descriptorFactoryMock.createForReferenceLoading(IDENTIFIER, etAMock)).thenReturn(typeAxiom);
- when(entityConstructorMock.createEntityInstance(IDENTIFIER, etAMock)).thenReturn(entityA);
-
- final OWLClassA result = instanceLoader.loadReference(loadingParameters);
- assertNull(result);
- verify(entityConstructorMock, never()).createEntityInstance(IDENTIFIER, etAMock);
- }
-
- @Test
- void loadReferenceThrowsStorageAccessExceptionOnDriverException() throws Exception {
- final Axiom typeAxiom =
- new AxiomImpl<>(NamedResource.create(IDENTIFIER), Assertion.createClassAssertion(false),
- new Value<>(NamedResource.create(
- Vocabulary.c_OwlClassA)));
- when(descriptorFactoryMock.createForReferenceLoading(IDENTIFIER, etAMock)).thenReturn(typeAxiom);
- when(connectionMock.contains(any(), any())).thenThrow(new OntoDriverException());
-
- assertThrows(StorageAccessException.class, () -> instanceLoader.loadReference(loadingParameters));
- verify(entityConstructorMock, never()).createEntityInstance(IDENTIFIER, etAMock);
- }
-
@Test
void loadEntityReloadsQueryAttributesWhenInstanceIsRetrievedFromCache() {
when(cacheMock.contains(OWLClassA.class, loadingParameters.getIdentifier(), descriptor)).thenReturn(true);
diff --git a/jopa-impl/src/test/java/cz/cvut/kbss/jopa/oom/ObjectOntologyMapperTest.java b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/oom/ObjectOntologyMapperTest.java
index d0c9f8d5e..eaf475c55 100644
--- a/jopa-impl/src/test/java/cz/cvut/kbss/jopa/oom/ObjectOntologyMapperTest.java
+++ b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/oom/ObjectOntologyMapperTest.java
@@ -30,7 +30,6 @@
import cz.cvut.kbss.jopa.exceptions.OWLEntityExistsException;
import cz.cvut.kbss.jopa.exceptions.StorageAccessException;
import cz.cvut.kbss.jopa.model.LoadState;
-import cz.cvut.kbss.jopa.sessions.cache.CacheManager;
import cz.cvut.kbss.jopa.model.MetamodelImpl;
import cz.cvut.kbss.jopa.model.SequencesVocabulary;
import cz.cvut.kbss.jopa.model.descriptors.Descriptor;
@@ -38,7 +37,10 @@
import cz.cvut.kbss.jopa.model.metamodel.EntityType;
import cz.cvut.kbss.jopa.model.metamodel.FieldSpecification;
import cz.cvut.kbss.jopa.oom.exception.UnpersistedChangeException;
+import cz.cvut.kbss.jopa.proxy.reference.EntityReferenceProxy;
+import cz.cvut.kbss.jopa.proxy.reference.EntityReferenceProxyGenerator;
import cz.cvut.kbss.jopa.sessions.AbstractUnitOfWork;
+import cz.cvut.kbss.jopa.sessions.cache.CacheManager;
import cz.cvut.kbss.jopa.sessions.cache.Descriptors;
import cz.cvut.kbss.jopa.sessions.descriptor.LoadStateDescriptor;
import cz.cvut.kbss.jopa.sessions.util.LoadStateDescriptorRegistry;
@@ -354,7 +356,7 @@ void loadSimpleListThrowsStorageAccessExceptionWhenOntoDriverExceptionIsThrown()
}
@Test
- void loadReferencedListThrowsStorageAccessExceptionWhenOntoDriverExceptionIsThrown() throws Exception {
+ void getReferencedListThrowsStorageAccessExceptionWhenOntoDriverExceptionIsThrown() throws Exception {
final String message = "OntoDriver exception was thrown";
final Lists listsMock = mock(Lists.class);
when(listsMock.loadReferencedList(any(ReferencedListDescriptor.class)))
@@ -380,7 +382,7 @@ void usesTwoStepInstanceLoaderForLoadingInstanceOfEntityWithSubtypes() throws Ex
aDescriptor);
doReturn(entity).when(twoStepLoader).loadEntity(loadingParameters);
final LoadStateDescriptor loadStateDescriptor = new LoadStateDescriptor<>(entity, mocks.forOwlClassS()
- .entityType(), LoadState.UNKNOWN);
+ .entityType(), LoadState.UNKNOWN);
loadStateRegistry.put(entity, loadStateDescriptor);
final OWLClassS result = mapper.loadEntity(loadingParameters);
@@ -409,7 +411,7 @@ void loadEntityDeterminesConcreteEntityTypeAndLoadsItFromCacheWhenItIsPresentThe
when(cacheMock.contains(OWLClassR.class, IDENTIFIER, aDescriptor)).thenReturn(true);
when(cacheMock.get(OWLClassR.class, IDENTIFIER, aDescriptor)).thenReturn(entity);
final LoadStateDescriptor loadStateDescriptor = new LoadStateDescriptor<>(entity, mocks.forOwlClassR()
- .entityType(), LoadState.UNKNOWN);
+ .entityType(), LoadState.UNKNOWN);
doReturn(loadStateDescriptor).when(cacheMock).getLoadStateDescriptor(entity);
final Types typesMock = mock(Types.class);
final NamedResource individual = NamedResource.create(IDENTIFIER);
@@ -637,75 +639,25 @@ void persistSavesPendingReferencedListContainingPersistedInstance() throws Excep
verify(listsMock).updateReferencedList(any());
}
- @Test
- void loadReferenceRetrievesEntityReferenceViaDefaultInstanceLoader() throws Exception {
- when(entityConstructorMock.createEntityInstance(IDENTIFIER, mocks.forOwlClassA().entityType()))
- .thenReturn(new OWLClassA(IDENTIFIER));
- final Axiom axiom = new AxiomImpl<>(NamedResource.create(IDENTIFIER),
- Assertion.createClassAssertion(false),
- new Value<>(NamedResource.create(Vocabulary.c_OwlClassA)));
- when(connectionMock.contains(axiom, Collections.emptySet())).thenReturn(true);
- final OWLClassA result = mapper
- .loadReference(new LoadingParameters<>(OWLClassA.class, IDENTIFIER, aDescriptor));
- assertNotNull(result);
- assertEquals(IDENTIFIER, result.getUri());
- assertNull(result.getStringAttribute());
- verify(connectionMock).contains(eq(axiom), eq(Collections.emptySet()));
- }
-
- @Test
- void loadReferenceUsesTwoStepLoaderWhenEntityTypeHasSubclasses() throws Exception {
- final Types typesMock = mock(Types.class);
- when(connectionMock.types()).thenReturn(typesMock);
- final NamedResource idResource = NamedResource.create(IDENTIFIER);
- final Set> typesAxioms = Collections.singleton(
- new AxiomImpl<>(idResource, Assertion.createClassAssertion(false),
- new Value<>(URI.create(Vocabulary.C_OWLClassR))));
- when(typesMock.getTypes(idResource, Collections.emptySet(), false)).thenReturn(typesAxioms);
- final OWLClassS result = mapper
- .loadReference(new LoadingParameters<>(OWLClassS.class, IDENTIFIER, new EntityDescriptor()));
- assertNotNull(result);
- assertInstanceOf(OWLClassR.class, result);
- verify(typesMock).getTypes(eq(idResource), eq(Collections.emptySet()), eq(false));
- }
-
- @Test
- void loadReferenceUsesContextWhenGettingDataFromConnection() throws Exception {
- final URI context = Generators.createIndividualIdentifier();
- final EntityDescriptor descriptor = new EntityDescriptor(context);
- when(entityConstructorMock.createEntityInstance(IDENTIFIER, mocks.forOwlClassA().entityType()))
- .thenReturn(new OWLClassA(IDENTIFIER));
- when(connectionMock.contains(
- new AxiomImpl<>(NamedResource.create(IDENTIFIER), Assertion.createClassAssertion(false),
- new Value<>(NamedResource.create(Vocabulary.c_OwlClassA))),
- Collections.singleton(context)))
- .thenReturn(true);
- final OWLClassA result = mapper
- .loadReference(new LoadingParameters<>(OWLClassA.class, IDENTIFIER, descriptor));
- assertNotNull(result);
- verify(connectionMock).contains(any(Axiom.class), eq(Collections.singleton(context)));
- }
-
- @Test
- void loadReferenceUsesContextWhenGettingTypesFromConnection() throws Exception {
- final URI context = Generators.createIndividualIdentifier();
- final EntityDescriptor descriptor = new EntityDescriptor(context);
- final Types typesMock = mock(Types.class);
- when(connectionMock.types()).thenReturn(typesMock);
- final NamedResource idResource = NamedResource.create(IDENTIFIER);
- final Set> typesAxioms = Collections.singleton(
- new AxiomImpl<>(idResource, Assertion.createClassAssertion(false),
- new Value<>(URI.create(Vocabulary.C_OWLClassR))));
- when(typesMock.getTypes(idResource, Collections.singleton(context), false)).thenReturn(typesAxioms);
- final OWLClassS result = mapper.loadReference(new LoadingParameters<>(OWLClassS.class, IDENTIFIER, descriptor));
- assertNotNull(result);
- verify(typesMock).getTypes(idResource, Collections.singleton(context), false);
- }
-
@Test
void getEntityFromCacheOrOntologyThrowsEntityExistsWhenObjectIsAlreadyRegisteredUnderDifferentType() {
mapper.registerInstance(IDENTIFIER, entityA);
assertThrows(OWLEntityExistsException.class,
() -> mapper.getEntityFromCacheOrOntology(OWLClassB.class, IDENTIFIER, aDescriptor));
}
+
+ @Test
+ void getReferenceReturnsReferenceObjectOfRequestedEntityTypeWithIdentifierSet() throws Exception {
+ when(metamodelMock.getEntityReferenceProxy(OWLClassA.class)).thenReturn((Class) new EntityReferenceProxyGenerator().generate(OWLClassA.class));
+ final OWLClassA result = mapper.getReference(loadingParameters);
+ assertNotNull(result);
+ assertEquals(IDENTIFIER, result.getUri());
+ assertInstanceOf(EntityReferenceProxy.class, result);
+ final EntityReferenceProxy proxy = (EntityReferenceProxy) result;
+ assertEquals(uowMock, proxy.getPersistenceContext());
+ assertEquals(aDescriptor, proxy.getDescriptor());
+ assertEquals(IDENTIFIER, proxy.getIdentifier());
+ assertEquals(OWLClassA.class, proxy.getType());
+ verify(connectionMock, never()).find(any());
+ }
}
diff --git a/jopa-impl/src/test/java/cz/cvut/kbss/jopa/oom/TwoStepInstanceLoaderTest.java b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/oom/TwoStepInstanceLoaderTest.java
index 9ab7cbcb2..87ccdf63d 100644
--- a/jopa-impl/src/test/java/cz/cvut/kbss/jopa/oom/TwoStepInstanceLoaderTest.java
+++ b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/oom/TwoStepInstanceLoaderTest.java
@@ -46,8 +46,12 @@
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.Mockito.*;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
@@ -133,41 +137,4 @@ void loadEntityThrowsStorageAccessExceptionWhenOntoDriverThrowsException() throw
assertThrows(StorageAccessException.class, () -> instanceLoader.loadEntity(loadingParameters));
assertThat(ex.getMessage(), containsString(msg));
}
-
- @Test
- void loadReferenceLoadsReferenceFromStorageWhenEntityTypeIsDetermined() throws Exception {
- final Axiom type = new AxiomImpl<>(INDIVIDUAL, Assertion.createClassAssertion(false),
- new Value<>(URI.create(OWLClassA.getClassIri())));
- when(typesMock.getTypes(INDIVIDUAL, Collections.emptySet(), false)).thenReturn(Collections.singleton(type));
- when(entityConstructorMock.createEntityInstance(IDENTIFIER, metamodelMock.entity(OWLClassA.class)))
- .thenReturn(new OWLClassA(IDENTIFIER));
-
- final OWLClassA result =
- instanceLoader.loadReference(new LoadingParameters<>(OWLClassA.class, IDENTIFIER, descriptor));
- assertNotNull(result);
- verify(typesMock).getTypes(INDIVIDUAL, Collections.emptySet(), false);
- }
-
- @Test
- void loadReferenceReturnsNullWhenTypeCannotBeResolved() throws Exception {
- when(typesMock.getTypes(INDIVIDUAL, null, false)).thenReturn(Collections.emptySet());
- when(entityConstructorMock.createEntityInstance(IDENTIFIER, metamodelMock.entity(OWLClassA.class)))
- .thenReturn(new OWLClassA(IDENTIFIER));
-
- final OWLClassA result =
- instanceLoader.loadReference(new LoadingParameters<>(OWLClassA.class, IDENTIFIER, descriptor));
- assertNull(result);
- verify(entityConstructorMock, never()).createEntityInstance(any(), any());
- }
-
- @Test
- void loadReferenceThrowsStorageAccessExceptionWhenOntoDriverExceptionIsThrown() throws Exception {
- final String msg = "Exception message.";
- when(typesMock.getTypes(INDIVIDUAL, Collections.emptySet(), false)).thenThrow(new OntoDriverException(msg));
-
- final StorageAccessException ex =
- assertThrows(StorageAccessException.class, () -> instanceLoader.loadReference(loadingParameters));
- assertThat(ex.getMessage(), containsString(msg));
- verify(entityConstructorMock, never()).createEntityInstance(any(), any());
- }
}
diff --git a/jopa-impl/src/test/java/cz/cvut/kbss/jopa/proxy/reference/EntityReferenceProxyGeneratorTest.java b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/proxy/reference/EntityReferenceProxyGeneratorTest.java
new file mode 100644
index 000000000..afd4db5c4
--- /dev/null
+++ b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/proxy/reference/EntityReferenceProxyGeneratorTest.java
@@ -0,0 +1,167 @@
+package cz.cvut.kbss.jopa.proxy.reference;
+
+import cz.cvut.kbss.jopa.environment.OWLClassA;
+import cz.cvut.kbss.jopa.environment.utils.Generators;
+import cz.cvut.kbss.jopa.exceptions.AttributeModificationForbiddenException;
+import cz.cvut.kbss.jopa.exceptions.EntityNotFoundException;
+import cz.cvut.kbss.jopa.model.MetamodelImpl;
+import cz.cvut.kbss.jopa.model.metamodel.IdentifiableEntityType;
+import cz.cvut.kbss.jopa.model.metamodel.Identifier;
+import cz.cvut.kbss.jopa.sessions.UnitOfWork;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class EntityReferenceProxyGeneratorTest {
+
+ @Mock
+ private UnitOfWork uow;
+
+ private final OWLClassA storedInstance = Generators.generateOwlClassAInstance();
+
+ private final EntityReferenceProxyGenerator sut = new EntityReferenceProxyGenerator();
+
+ @Test
+ void generateGeneratesProxyClassImplementingCorrectInterfaces() {
+ final Class extends OWLClassA> result = sut.generate(OWLClassA.class);
+ assertTrue(EntityReferenceProxy.class.isAssignableFrom(result));
+ }
+
+ @Test
+ void generateGeneratesProxyClassThatTriggersLoadingWhenGetterIsAccessedAndReturnsLoadedValue() throws Exception {
+ final Class extends OWLClassA> result = sut.generate(OWLClassA.class);
+ final OWLClassA instance = result.getConstructor().newInstance();
+ initReferenceLoading(instance);
+ initMetamodel();
+ assertEquals(storedInstance.getStringAttribute(), instance.getStringAttribute());
+ verify(uow).readObject(OWLClassA.class, storedInstance.getUri(), null);
+ }
+
+ private void initReferenceLoading(OWLClassA instance) {
+ assertInstanceOf(EntityReferenceProxy.class, instance);
+ final EntityReferenceProxy aInstance = (EntityReferenceProxy) instance;
+ aInstance.setPersistenceContext(uow);
+ aInstance.setIdentifier(storedInstance.getUri());
+ aInstance.setType(OWLClassA.class);
+ when(uow.readObject(OWLClassA.class, storedInstance.getUri(), null)).thenReturn(storedInstance);
+ when(uow.isActive()).thenReturn(true);
+ }
+
+ private void initMetamodel() throws Exception {
+ final MetamodelImpl mm = mock(MetamodelImpl.class);
+ when(uow.getMetamodel()).thenReturn(mm);
+ final IdentifiableEntityType et = mock(IdentifiableEntityType.class);
+ when(mm.entity(OWLClassA.class)).thenReturn(et);
+ final Identifier id = mock(Identifier.class);
+ when(id.getName()).thenReturn(OWLClassA.class.getDeclaredField("uri").getName());
+ when(et.getIdentifier()).thenReturn(id);
+ }
+
+ @Test
+ void generateGeneratesProxyClassThatReturnsLoadedValueOnSubsequentGetterCalls() throws Exception {
+ final Class extends OWLClassA> result = sut.generate(OWLClassA.class);
+ final OWLClassA instance = result.getConstructor().newInstance();
+ initReferenceLoading(instance);
+ initMetamodel();
+ assertEquals(storedInstance.getStringAttribute(), instance.getStringAttribute());
+ assertTrue(((EntityReferenceProxy) instance).isLoaded());
+ assertEquals(storedInstance.getStringAttribute(), instance.getStringAttribute());
+ assertEquals(storedInstance.getStringAttribute(), instance.getStringAttribute());
+ verify(uow).readObject(OWLClassA.class, storedInstance.getUri(), null);
+ }
+
+ @Test
+ void generateGeneratesProxyClassThatTriggersLoadingWhenSetterIsAccessedAndPassesValueToLoadedInstance() throws Exception {
+ final Class extends OWLClassA> result = sut.generate(OWLClassA.class);
+ final OWLClassA instance = result.getConstructor().newInstance();
+ initReferenceLoading(instance);
+ initMetamodel();
+ final String newString = "random string " + Generators.randomInt();
+ instance.setStringAttribute(newString);
+ verify(uow).readObject(OWLClassA.class, storedInstance.getUri(), null);
+ assertEquals(newString, storedInstance.getStringAttribute());
+ }
+
+ @Test
+ void generateGeneratesProxyClassThatUsesLoadedInstanceOnSubsequentSetterCalls() throws Exception {
+ final Class extends OWLClassA> result = sut.generate(OWLClassA.class);
+ final OWLClassA instance = result.getConstructor().newInstance();
+ initReferenceLoading(instance);
+ initMetamodel();
+ final List values = List.of(Integer.toString(Generators.randomInt()), Integer.toString(Generators.randomInt()), Integer.toString(Generators.randomInt()));
+ values.forEach(instance::setStringAttribute);
+ verify(uow).readObject(OWLClassA.class, storedInstance.getUri(), null);
+ assertEquals(values.get(values.size() - 1), instance.getStringAttribute());
+ assertEquals(values.get(values.size() - 1), storedInstance.getStringAttribute());
+ }
+
+ @Test
+ void generateGeneratesProxyClassThatThrowsEntityNotFoundExceptionWhenLoadingOnGetterCallReturnsNull() throws Exception {
+ initMetamodel();
+ final Class extends OWLClassA> result = sut.generate(OWLClassA.class);
+ final OWLClassA instance = result.getConstructor().newInstance();
+ final EntityReferenceProxy aInstance = (EntityReferenceProxy) instance;
+ aInstance.setPersistenceContext(uow);
+ aInstance.setIdentifier(storedInstance.getUri());
+ aInstance.setType(OWLClassA.class);
+ when(uow.isActive()).thenReturn(true);
+ assertThrows(EntityNotFoundException.class, instance::getStringAttribute);
+ }
+
+ @Test
+ void generateGeneratesProxyClassThatThrowsEntityNotFoundExceptionWhenLoadingOnSetterCallReturnsNull() throws Exception {
+ initMetamodel();
+ final Class extends OWLClassA> result = sut.generate(OWLClassA.class);
+ final OWLClassA instance = result.getConstructor().newInstance();
+ final EntityReferenceProxy aInstance = (EntityReferenceProxy) instance;
+ aInstance.setPersistenceContext(uow);
+ aInstance.setIdentifier(storedInstance.getUri());
+ aInstance.setType(OWLClassA.class);
+ when(uow.isActive()).thenReturn(true);
+ assertThrows(EntityNotFoundException.class, () -> instance.setTypes(Set.of(Generators.createIndividualIdentifier()
+ .toString())));
+ }
+
+ @Test
+ void generateGeneratesProxyClassThatDoesNotTriggerLoadingOnIdentifierGetter() throws Exception {
+ initMetamodel();
+ final Class extends OWLClassA> result = sut.generate(OWLClassA.class);
+ final OWLClassA instance = result.getConstructor().newInstance();
+ final EntityReferenceProxy aInstance = (EntityReferenceProxy) instance;
+ aInstance.setPersistenceContext(uow);
+ aInstance.setIdentifier(storedInstance.getUri());
+ aInstance.setType(OWLClassA.class);
+ assertEquals(storedInstance.getUri(), instance.getUri());
+ verify(uow, never()).readObject(OWLClassA.class, storedInstance.getUri(), null);
+ }
+
+ // This makes no sense, but let's be sure we are handling it
+ @Test
+ void generateGeneratesProxyClassThatThrowsAttributeModificationForbiddenExceptionOnIdentifierSetter() throws Exception {
+ initMetamodel();
+ final Class extends OWLClassA> result = sut.generate(OWLClassA.class);
+ final OWLClassA instance = result.getConstructor().newInstance();
+ final EntityReferenceProxy aInstance = (EntityReferenceProxy) instance;
+ aInstance.setPersistenceContext(uow);
+ aInstance.setIdentifier(storedInstance.getUri());
+ aInstance.setType(OWLClassA.class);
+ final URI newId = Generators.createIndividualIdentifier();
+ assertThrows(AttributeModificationForbiddenException.class, () -> instance.setUri(newId));
+ verify(uow, never()).readObject(OWLClassA.class, storedInstance.getUri(), null);
+ }
+}
diff --git a/jopa-impl/src/test/java/cz/cvut/kbss/jopa/query/soql/SoqlQueryParserTest.java b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/query/soql/SoqlQueryParserTest.java
index e6e663fa7..58b3c3a90 100644
--- a/jopa-impl/src/test/java/cz/cvut/kbss/jopa/query/soql/SoqlQueryParserTest.java
+++ b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/query/soql/SoqlQueryParserTest.java
@@ -793,4 +793,14 @@ void parseQueryTranslatesNotMemberOfToFilterNotExists() {
final String expectedSparql = "SELECT ?x WHERE { ?x a " + strUri(Vocabulary.c_Person) + " . FILTER NOT EXISTS { ?x a ?disabledType . } }";
parseAndAssertEquality(soql, expectedSparql);
}
+
+ /**
+ * Bug #234
+ */
+ @Test
+ void parseQueryTranslatesQueryUsingRootIdentifierAfterReferenceIdentifier() {
+ final String soql = "SELECT DISTINCT h FROM OWLClassH h WHERE h.owlClassA.uri = :aUri AND h.owlClassG.uri = :gUri AND h.uri = :hUri";
+ final String expectedSparql = "SELECT DISTINCT ?hUri WHERE { ?hUri a " + strUri(Vocabulary.c_OwlClassH) + " . ?hUri " + strUri(Vocabulary.p_h_hasA) + " ?aUri . ?hUri " + strUri(Vocabulary.p_h_hasG) + " ?gUri . }";
+ parseAndAssertEquality(soql, expectedSparql);
+ }
}
diff --git a/jopa-impl/src/test/java/cz/cvut/kbss/jopa/sessions/AbstractUnitOfWorkGetReferenceTestRunner.java b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/sessions/AbstractUnitOfWorkGetReferenceTestRunner.java
new file mode 100644
index 000000000..14458be46
--- /dev/null
+++ b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/sessions/AbstractUnitOfWorkGetReferenceTestRunner.java
@@ -0,0 +1,168 @@
+/*
+ * JOPA
+ * Copyright (C) 2023 Czech Technical University in Prague
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3.0 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.
+ */
+package cz.cvut.kbss.jopa.sessions;
+
+import cz.cvut.kbss.jopa.environment.OWLClassA;
+import cz.cvut.kbss.jopa.environment.OWLClassD;
+import cz.cvut.kbss.jopa.environment.utils.Generators;
+import cz.cvut.kbss.jopa.exceptions.EntityNotFoundException;
+import cz.cvut.kbss.jopa.exceptions.OWLEntityExistsException;
+import cz.cvut.kbss.jopa.model.EntityState;
+import cz.cvut.kbss.jopa.proxy.reference.EntityReferenceProxy;
+import cz.cvut.kbss.jopa.proxy.reference.EntityReferenceProxyGenerator;
+import cz.cvut.kbss.jopa.sessions.util.LoadingParameters;
+import org.junit.jupiter.api.Test;
+
+import java.lang.reflect.Field;
+import java.net.URI;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public abstract class AbstractUnitOfWorkGetReferenceTestRunner extends UnitOfWorkTestBase {
+
+ @Test
+ void getReferenceReturnsExistingCloneWhenItIsAlreadyManaged() {
+ final OWLClassA existing = (OWLClassA) uow.registerExistingObject(entityA, descriptor);
+ final OWLClassA result = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
+ assertNotNull(result);
+ assertSame(existing, result);
+ }
+
+ @Test
+ void getReferenceReturnsExistingNewlyRegisteredObject() {
+ uow.registerNewObject(entityA, descriptor);
+ final OWLClassA result = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
+ assertNotNull(result);
+ assertSame(entityA, result);
+ }
+
+ @Test
+ void getReferenceThrowsEntityNotFoundExceptionWhenObjectIsScheduledForDeletion() {
+ final OWLClassA existing = (OWLClassA) uow.registerExistingObject(entityA, descriptor);
+ uow.removeObject(existing);
+ assertThrows(EntityNotFoundException.class, () -> uow.getReference(OWLClassA.class, entityA.getUri(), descriptor));
+ }
+
+ @Test
+ void getReferenceThrowsEntityExistsExceptionWhenIndividualIsAlreadyManagedAsDifferentIncompatibleType() {
+ uow.registerExistingObject(entityA, descriptor);
+ assertThrows(OWLEntityExistsException.class,
+ () -> uow.getReference(OWLClassD.class, entityA.getUri(), descriptor));
+ }
+
+ @Test
+ void getReferenceGetsReferenceFromStorageWhenItIsNotManaged() {
+ final OWLClassA reference = createReference(entityA.getUri());
+ when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
+ final OWLClassA result = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
+ assertNotNull(result);
+ assertEquals(reference.getUri(), result.getUri());
+ verify(storageMock).getReference(new LoadingParameters<>(OWLClassA.class, entityA.getUri(), descriptor));
+ }
+
+ OWLClassA createReference(URI identifier) {
+ final Class extends OWLClassA> proxyClass = new EntityReferenceProxyGenerator().generate(OWLClassA.class);
+ try {
+ final OWLClassA reference = proxyClass.getDeclaredConstructor().newInstance();
+ final EntityReferenceProxy proxy = (EntityReferenceProxy) reference;
+ proxy.setDescriptor(descriptor);
+ proxy.setType(OWLClassA.class);
+ proxy.setPersistenceContext(uow);
+ proxy.setIdentifier(identifier);
+ final Field idField = OWLClassA.class.getDeclaredField("uri");
+ idField.setAccessible(true);
+ idField.set(reference, identifier);
+ return reference;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Test
+ void containsReturnsTrueForInstanceRetrievedUsingGetReference() {
+ final OWLClassA reference = createReference(entityA.getUri());
+ when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
+ final OWLClassA result = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
+ assertTrue(uow.contains(result));
+ }
+
+ @Test
+ void getStateReturnsManagedForInstanceRetrieveUsingGetReference() {
+ final OWLClassA reference = createReference(entityA.getUri());
+ when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
+ final OWLClassA result = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
+ assertEquals(EntityState.MANAGED, uow.getState(result));
+ assertEquals(EntityState.MANAGED, uow.getState(result, descriptor));
+ }
+
+ @Test
+ void removeOfObjectRetrievedUsingGetReferenceEvictsCorrespondingInstanceFromCacheOnCommit() {
+ when(transactionMock.isActive()).thenReturn(true);
+ final OWLClassA reference = createReference(entityA.getUri());
+ when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
+ final OWLClassA result = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
+ uow.removeObject(result);
+ uow.commit();
+ verify(serverSessionStub.getLiveObjectCache()).evict(OWLClassA.class, reference.getUri(), descriptor.getSingleContext()
+ .orElse(null));
+ }
+
+ @Test
+ void changesToGetReferenceResultAreMergedIntoOriginalInCacheOnCommit() {
+ when(transactionMock.isActive()).thenReturn(true);
+ when(serverSessionStub.getLiveObjectCache()
+ .contains(OWLClassA.class, entityA.getUri(), descriptor)).thenReturn(true);
+ when(serverSessionStub.getLiveObjectCache()
+ .get(OWLClassA.class, entityA.getUri(), descriptor)).thenReturn(entityA);
+ final OWLClassA reference = createReference(entityA.getUri());
+ when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
+ when(storageMock.find(any(LoadingParameters.class))).thenReturn(entityA);
+ final OWLClassA a = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
+ final String strValue = "string value";
+ a.setStringAttribute(strValue);
+ uow.commit();
+ assertEquals(strValue, entityA.getStringAttribute());
+ }
+
+ @Test
+ void mergeDetachedMarksChangeRecordForAttributeWithGetReferenceResultAsPreventingCaching() {
+ when(transactionMock.isActive()).thenReturn(true);
+ final OWLClassA reference = createReference(entityA.getUri());
+ when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
+ final OWLClassA ref = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
+ final OWLClassD owner = new OWLClassD(Generators.createIndividualIdentifier());
+ final OWLClassD toMerge = new OWLClassD(owner.getUri());
+ when(storageMock.find(any(LoadingParameters.class))).thenReturn(owner);
+ when(storageMock.contains(owner.getUri(), OWLClassD.class, descriptor)).thenReturn(true);
+ toMerge.setOwlClassA(ref);
+
+ uow.mergeDetached(toMerge, descriptor);
+ uow.commit();
+
+ verify(serverSessionStub.getLiveObjectCache(), never()).add(eq(toMerge.getUri()), eq(owner), any());
+ }
+}
diff --git a/jopa-impl/src/test/java/cz/cvut/kbss/jopa/sessions/AbstractUnitOfWorkTestRunner.java b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/sessions/AbstractUnitOfWorkTestRunner.java
index 9170d6c81..b2e8822e6 100644
--- a/jopa-impl/src/test/java/cz/cvut/kbss/jopa/sessions/AbstractUnitOfWorkTestRunner.java
+++ b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/sessions/AbstractUnitOfWorkTestRunner.java
@@ -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
diff --git a/jopa-impl/src/test/java/cz/cvut/kbss/jopa/sessions/ChangeTrackingUnitOfWorkGetReferenceTest.java b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/sessions/ChangeTrackingUnitOfWorkGetReferenceTest.java
new file mode 100644
index 000000000..9b2cf51b3
--- /dev/null
+++ b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/sessions/ChangeTrackingUnitOfWorkGetReferenceTest.java
@@ -0,0 +1,67 @@
+package cz.cvut.kbss.jopa.sessions;
+
+import cz.cvut.kbss.jopa.environment.OWLClassA;
+import cz.cvut.kbss.jopa.model.EntityState;
+import cz.cvut.kbss.jopa.sessions.util.LoadingParameters;
+import cz.cvut.kbss.jopa.utils.Configuration;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.mockito.junit.jupiter.MockitoSettings;
+import org.mockito.quality.Strictness;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+@MockitoSettings(strictness = Strictness.LENIENT)
+public class ChangeTrackingUnitOfWorkGetReferenceTest extends AbstractUnitOfWorkGetReferenceTestRunner {
+
+ @BeforeEach
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected AbstractUnitOfWork initUnitOfWork() {
+ return new ChangeTrackingUnitOfWork(serverSessionStub, new Configuration());
+ }
+
+ @Test
+ void attributeChangedPropagatesChangeOfInstanceRetrievedUsingGetReferenceIntoRepository() throws Exception {
+ when(transactionMock.isActive()).thenReturn(true);
+ final OWLClassA reference = createReference(entityA.getUri());
+ when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
+ final OWLClassA result = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
+ uow.attributeChanged(result, OWLClassA.getStrAttField());
+ verify(storageMock).merge(result, metamodelMocks.forOwlClassA().stringAttribute(), descriptor);
+ }
+
+ @Test
+ void attributeChangedDoesNotRegisterChangeForMergingIntoCachedOriginalForInstanceRetrievedUsingGetReference() throws Exception {
+ when(transactionMock.isActive()).thenReturn(true);
+ final OWLClassA reference = createReference(entityA.getUri());
+ when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
+ final OWLClassA result = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
+ uow.attributeChanged(result, OWLClassA.getStrAttField());
+ assertFalse(uow.uowChangeSet.hasChanges());
+ }
+
+ @Test
+ void removeRemovesInstanceRetrievedUsingGetReferenceFromRepository() {
+ when(transactionMock.isActive()).thenReturn(true);
+ final OWLClassA reference = createReference(entityA.getUri());
+ when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
+ final OWLClassA result = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
+ assertTrue(uow.contains(result));
+ uow.removeObject(result);
+ assertFalse(uow.contains(result));
+ assertEquals(EntityState.REMOVED, uow.getState(result));
+ verify(storageMock).remove(reference.getUri(), OWLClassA.class, descriptor);
+ }
+}
diff --git a/jopa-impl/src/test/java/cz/cvut/kbss/jopa/sessions/OnCommitChangePropagatingUnitOfWorkGetReferenceTest.java b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/sessions/OnCommitChangePropagatingUnitOfWorkGetReferenceTest.java
new file mode 100644
index 000000000..2324280b1
--- /dev/null
+++ b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/sessions/OnCommitChangePropagatingUnitOfWorkGetReferenceTest.java
@@ -0,0 +1,73 @@
+package cz.cvut.kbss.jopa.sessions;
+
+import cz.cvut.kbss.jopa.environment.OWLClassA;
+import cz.cvut.kbss.jopa.model.EntityState;
+import cz.cvut.kbss.jopa.sessions.util.LoadingParameters;
+import cz.cvut.kbss.jopa.utils.Configuration;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.mockito.junit.jupiter.MockitoSettings;
+import org.mockito.quality.Strictness;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+@MockitoSettings(strictness = Strictness.LENIENT)
+public class OnCommitChangePropagatingUnitOfWorkGetReferenceTest extends AbstractUnitOfWorkGetReferenceTestRunner {
+
+ @BeforeEach
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected AbstractUnitOfWork initUnitOfWork() {
+ return new OnCommitChangePropagatingUnitOfWork(serverSessionStub, new Configuration());
+ }
+
+ @Test
+ void removeOfInstanceRetrievedUsingGetReferenceSchedulesItForDeletion() {
+ final OWLClassA reference = createReference(entityA.getUri());
+ when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
+ final OWLClassA result = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
+ assertTrue(uow.contains(result));
+ uow.removeObject(result);
+ assertFalse(uow.contains(result));
+ assertEquals(EntityState.REMOVED, uow.getState(result));
+ }
+
+ @Test
+ void removeOfInstanceRetrievedUsingGetReferenceRemovesItFromStorageOnCommit() {
+ when(transactionMock.isActive()).thenReturn(true);
+ final OWLClassA reference = createReference(entityA.getUri());
+ when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
+ final OWLClassA result = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
+ uow.removeObject(result);
+ verify(storageMock, never()).remove(entityA.getUri(), OWLClassA.class, descriptor);
+ uow.commit();
+ verify(storageMock).remove(entityA.getUri(), OWLClassA.class, descriptor);
+ }
+
+ @Test
+ void changeOfAttributeValueOfInstanceRetrievedByGetReferenceIsPropagatedToStorageOnCommit() {
+ when(transactionMock.isActive()).thenReturn(true);
+ final OWLClassA reference = createReference(entityA.getUri());
+ when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
+ when(storageMock.find(any(LoadingParameters.class))).thenReturn(entityA);
+ final OWLClassA entity = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
+ final String newStringValue = "New string attribute value";
+ entity.setStringAttribute(newStringValue);
+ verify(storageMock, never()).merge(any(), eq(metamodelMocks.forOwlClassA().stringAttribute()), eq(descriptor));
+ uow.commit();
+ verify(storageMock).merge(any(), eq(metamodelMocks.forOwlClassA().stringAttribute()), eq(descriptor));
+ }
+}
diff --git a/jopa-impl/src/test/java/cz/cvut/kbss/jopa/sessions/UnitOfWorkGetReferenceTest.java b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/sessions/UnitOfWorkGetReferenceTest.java
deleted file mode 100644
index b02a4b8bf..000000000
--- a/jopa-impl/src/test/java/cz/cvut/kbss/jopa/sessions/UnitOfWorkGetReferenceTest.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * JOPA
- * Copyright (C) 2023 Czech Technical University in Prague
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3.0 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library.
- */
-package cz.cvut.kbss.jopa.sessions;
-
-import cz.cvut.kbss.jopa.environment.OWLClassA;
-import cz.cvut.kbss.jopa.environment.OWLClassD;
-import cz.cvut.kbss.jopa.environment.OWLClassL;
-import cz.cvut.kbss.jopa.environment.utils.Generators;
-import cz.cvut.kbss.jopa.exceptions.OWLEntityExistsException;
-import cz.cvut.kbss.jopa.model.EntityState;
-import cz.cvut.kbss.jopa.model.LoadState;
-import cz.cvut.kbss.jopa.sessions.change.ChangeRecord;
-import cz.cvut.kbss.jopa.sessions.change.ObjectChangeSet;
-import cz.cvut.kbss.jopa.sessions.util.LoadingParameters;
-import cz.cvut.kbss.jopa.utils.Configuration;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.mockito.junit.jupiter.MockitoSettings;
-import org.mockito.quality.Strictness;
-
-import java.util.Optional;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertSame;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-@ExtendWith(MockitoExtension.class)
-@MockitoSettings(strictness = Strictness.LENIENT)
-@Disabled
-public class UnitOfWorkGetReferenceTest extends UnitOfWorkTestBase {
-
- // TODO Support ChangeTrackingUoW and OnCommitChangePropagatingUoW
-
- @BeforeEach
- protected void setUp() throws Exception {
- super.setUp();
- }
-
- @Override
- protected AbstractUnitOfWork initUnitOfWork() {
- return new ChangeTrackingUnitOfWork(serverSessionStub, new Configuration());
- }
-
- @Test
- void getReferenceReturnsExistingCloneWhenItIsAlreadyManaged() {
- final OWLClassA existing = (OWLClassA) uow.registerExistingObject(entityA, descriptor);
- final OWLClassA result = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
- assertNotNull(result);
- assertSame(existing, result);
- }
-
- @Test
- void getReferenceReturnsExistingNewlyRegisteredObject() {
- uow.registerNewObject(entityA, descriptor);
- final OWLClassA result = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
- assertNotNull(result);
- assertSame(entityA, result);
- }
-
- @Test
- void getReferenceReturnsNullWhenObjectIsScheduledForDeletion() {
- final OWLClassA existing = (OWLClassA) uow.registerExistingObject(entityA, descriptor);
- uow.removeObject(existing);
- assertNull(uow.getReference(OWLClassA.class, entityA.getUri(), descriptor));
- }
-
- @Test
- void getReferenceThrowsEntityExistsExceptionWhenIndividualIsAlreadyManagedAsDifferentIncompatibleType() {
- uow.registerExistingObject(entityA, descriptor);
- assertThrows(OWLEntityExistsException.class,
- () -> uow.getReference(OWLClassD.class, entityA.getUri(), descriptor));
- }
-
- @Test
- void getReferenceLoadsReferenceFromStorageWhenItIsNotManaged() {
- final OWLClassA reference = new OWLClassA(entityA.getUri());
- when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
- final OWLClassA result = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
- assertNotNull(result);
- assertEquals(reference.getUri(), result.getUri());
- verify(storageMock).getReference(new LoadingParameters<>(OWLClassA.class, entityA.getUri(), descriptor));
- }
-
- @Test
- void containsReturnsTrueForInstanceRetrievedUsingGetReference() {
- final OWLClassA reference = new OWLClassA(entityA.getUri());
- when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
- final OWLClassA result = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
- assertTrue(uow.contains(result));
- }
-
- @Test
- void getStateReturnsManagedForInstanceRetrieveUsingGetReference() {
- final OWLClassA reference = new OWLClassA(entityA.getUri());
- when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
- final OWLClassA result = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
- assertEquals(EntityState.MANAGED, uow.getState(result));
- assertEquals(EntityState.MANAGED, uow.getState(result, descriptor));
- }
-
- @Test
- void removeOfInstanceRetrievedUsingGetReferenceSchedulesItForDeletion() {
- final OWLClassA reference = new OWLClassA(entityA.getUri());
- when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
- final OWLClassA result = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
- assertTrue(uow.contains(result));
- uow.removeObject(result);
- assertFalse(uow.contains(result));
- assertEquals(EntityState.REMOVED, uow.getState(result));
- verify(storageMock).remove(entityA.getUri(), OWLClassA.class, descriptor);
- }
-
- @Test
- void loadEntityFieldLoadsValueOfAttributeOfInstanceRetrievedUsingGetReference() throws Exception {
- final OWLClassL reference = new OWLClassL(entityL.getUri());
- when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
- final OWLClassL result = uow.getReference(OWLClassL.class, entityL.getUri(), descriptor);
- uow.loadEntityField(result, metamodelMocks.forOwlClassL().setAttribute());
- verify(storageMock).loadFieldValue(result, metamodelMocks.forOwlClassL().setAttribute(), descriptor);
- assertEquals(LoadState.LOADED, uow.isLoaded(result, OWLClassL.getSetField().getName()));
- }
-
- @Test
- void loadEntityFieldDoesNothingWhenLazilyLoadedAttributeOfInstanceRetrievedUsingGetReferenceIsAlreadyLoaded()
- throws Exception {
- final OWLClassL reference = new OWLClassL(entityL.getUri());
- when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
- final OWLClassL result = uow.getReference(OWLClassL.class, entityL.getUri(), descriptor);
- uow.loadEntityField(result, metamodelMocks.forOwlClassL().setAttribute());
- // Call it twice. Storage should be called only once
- uow.loadEntityField(result, metamodelMocks.forOwlClassL().setAttribute());
- verify(storageMock).loadFieldValue(result, metamodelMocks.forOwlClassL().setAttribute(), descriptor);
- assertEquals(LoadState.LOADED, uow.isLoaded(result, OWLClassL.getSetField().getName()));
- }
-
- @Test
- void attributeChangedPropagatesChangeOfInstanceRetrievedUsingGetReferenceIntoRepository() throws Exception {
- when(transactionMock.isActive()).thenReturn(true);
- final OWLClassA reference = new OWLClassA(entityA.getUri());
- when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
- final OWLClassA result = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
- uow.attributeChanged(result, OWLClassA.getStrAttField());
- verify(storageMock).merge(result, metamodelMocks.forOwlClassA().stringAttribute(), descriptor);
- }
-
- @Test
- void attributeChangedDoesNotRegisterChangeForInstanceRetrievedUsingGetReference() throws Exception {
- when(transactionMock.isActive()).thenReturn(true);
- final OWLClassA reference = new OWLClassA(entityA.getUri());
- when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
- final OWLClassA result = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
- uow.attributeChanged(result, OWLClassA.getStrAttField());
- assertFalse(uow.uowChangeSet.hasChanges());
- }
-
- @Test
- void removeRemovesInstanceRetrievedUsingGetReferenceFromRepository() {
- when(transactionMock.isActive()).thenReturn(true);
- final OWLClassA reference = new OWLClassA(entityA.getUri());
- when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
- final OWLClassA result = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
- assertTrue(uow.contains(result));
- uow.removeObject(result);
- assertFalse(uow.contains(result));
- verify(storageMock).remove(reference.getUri(), OWLClassA.class, descriptor);
- }
-
- @Test
- void removeCreatesRemoveChangeOnCommitForInstanceRetrievedUsingGetReference() {
- when(transactionMock.isActive()).thenReturn(true);
- final OWLClassA reference = new OWLClassA(entityA.getUri());
- when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
- final OWLClassA result = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
- uow.removeObject(result);
- uow.commit();
- verify(serverSessionStub.getLiveObjectCache()).evict(OWLClassA.class, reference.getUri(), descriptor.getSingleContext().orElse(null));
- }
-
- @Test
- void getReferenceLoadsOriginalFromSecondLevelCacheWhenPresent() {
- when(serverSessionStub.getLiveObjectCache().contains(OWLClassA.class, entityA.getUri(), descriptor)).thenReturn(true);
- when(serverSessionStub.getLiveObjectCache().get(OWLClassA.class, entityA.getUri(), descriptor)).thenReturn(entityA);
- final OWLClassA reference = new OWLClassA(entityA.getUri());
- when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
- final OWLClassA result = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
- assertEquals(entityA, uow.getOriginal(result));
- }
-
- @Test
- void changesToGetReferenceResultAreMergedIntoOriginalInCache() {
- when(transactionMock.isActive()).thenReturn(true);
- when(serverSessionStub.getLiveObjectCache().contains(OWLClassA.class, entityA.getUri(), descriptor)).thenReturn(true);
- when(serverSessionStub.getLiveObjectCache().get(OWLClassA.class, entityA.getUri(), descriptor)).thenReturn(entityA);
- final OWLClassA reference = new OWLClassA(entityA.getUri());
- when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
- final OWLClassA a = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
- final String strValue = "string value";
- a.setStringAttribute(strValue);
- uow.commit();
- assertEquals(strValue, entityA.getStringAttribute());
- }
-
- @Test
- void uowCommitEvictsInstanceRetrievedUsingGetReferenceFromCacheWhenItWasNotPresentThereOnRetrieval() {
- when(transactionMock.isActive()).thenReturn(true);
- when(serverSessionStub.getLiveObjectCache().contains(OWLClassA.class, entityA.getUri(), descriptor)).thenReturn(false);
- final OWLClassA reference = new OWLClassA(entityA.getUri());
- when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
- final OWLClassA a = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
- final String strValue = "string value";
- a.setStringAttribute(strValue);
- uow.commit();
- verify(serverSessionStub.getLiveObjectCache()).evict(OWLClassA.class, entityA.getUri(), descriptor.getSingleContext().orElse(null));
- }
-
- @Test
- void attributeChangeSetsChangeRecordToPreventCachingWhenNewValueWasRetrievedUsingGetReference() throws Exception {
- when(transactionMock.isActive()).thenReturn(true);
- final OWLClassA reference = new OWLClassA(entityA.getUri());
- final OWLClassD owner = new OWLClassD(Generators.createIndividualIdentifier());
- when(storageMock.find(any(LoadingParameters.class))).thenReturn(owner);
- when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
- final OWLClassD changed = uow.readObject(OWLClassD.class, owner.getUri(), descriptor);
- final OWLClassA ref = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
- changed.setOwlClassA(ref);
-
- uow.attributeChanged(changed, OWLClassD.getOwlClassAField());
-
- final ObjectChangeSet changeSet = uow.uowChangeSet.getExistingObjectChanges(owner);
- assertFalse(changeSet.getChanges().isEmpty());
- final Optional changeRecord =
- changeSet.getChanges().stream().filter(chr -> chr.getNewValue().equals(ref)).findFirst();
- assertTrue(changeRecord.isPresent());
- assertTrue(changeRecord.get().doesPreventCaching());
- }
-
- @Test
- void mergeDetachedMarksChangeRecordForAttributeWithGetReferenceResultAsPreventingCaching() {
- when(transactionMock.isActive()).thenReturn(true);
- final OWLClassA reference = new OWLClassA(entityA.getUri());
- when(storageMock.getReference(any(LoadingParameters.class))).thenReturn(reference);
- final OWLClassA ref = uow.getReference(OWLClassA.class, entityA.getUri(), descriptor);
- final OWLClassD owner = new OWLClassD(Generators.createIndividualIdentifier());
- final OWLClassD toMerge = new OWLClassD(owner.getUri());
- when(storageMock.find(any(LoadingParameters.class))).thenReturn(owner);
- when(storageMock.contains(owner.getUri(), OWLClassD.class, descriptor)).thenReturn(true);
- toMerge.setOwlClassA(ref);
-
- uow.mergeDetached(toMerge, descriptor);
-
- final ObjectChangeSet changeSet = uow.uowChangeSet.getExistingObjectChanges(owner);
- assertFalse(changeSet.getChanges().isEmpty());
- final Optional changeRecord =
- changeSet.getChanges().stream().filter(chr -> chr.getNewValue().equals(ref)).findFirst();
- assertTrue(changeRecord.isPresent());
- assertTrue(changeRecord.get().doesPreventCaching());
- }
-}
diff --git a/jopa-impl/src/test/java/cz/cvut/kbss/jopa/sessions/merge/ManagedTypeValueMergerTest.java b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/sessions/merge/ManagedTypeValueMergerTest.java
index 2aa70cdf5..e88cf69c0 100644
--- a/jopa-impl/src/test/java/cz/cvut/kbss/jopa/sessions/merge/ManagedTypeValueMergerTest.java
+++ b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/sessions/merge/ManagedTypeValueMergerTest.java
@@ -40,6 +40,7 @@
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -130,4 +131,17 @@ void mergeReplacesNewValueInChangeRecordWhenItIsReadFromUoW() {
assertEquals(loaded, target.getOwlClassA());
assertEquals(loaded, changeRecord.getNewValue());
}
+
+ @Test
+ void mergeUsesValueWhenItIsAlreadyManagedByUoW() {
+ final OWLClassD target = new OWLClassD(Generators.createIndividualIdentifier());
+ final OWLClassA value = Generators.generateOwlClassAInstance();
+ when(uow.contains(value)).thenReturn(true);
+ target.setOwlClassA(value);
+ final ChangeRecord changeRecord = new ChangeRecord(refASpec, value);
+
+ sut.mergeValue(target, changeRecord, descriptor);
+ assertSame(value, target.getOwlClassA());
+ verify(uow, never()).readObject(OWLClassA.class, value.getUri(), descriptor);
+ }
}
diff --git a/jopa-integration-tests-jena/pom.xml b/jopa-integration-tests-jena/pom.xml
index 8d059a483..9ea01355d 100644
--- a/jopa-integration-tests-jena/pom.xml
+++ b/jopa-integration-tests-jena/pom.xml
@@ -5,7 +5,7 @@
cz.cvut.kbss.jopa
jopa-all
- 2.0.0
+ 2.0.1
../pom.xml
4.0.0
diff --git a/jopa-integration-tests-owlapi/pom.xml b/jopa-integration-tests-owlapi/pom.xml
index e762433cd..e295167b3 100644
--- a/jopa-integration-tests-owlapi/pom.xml
+++ b/jopa-integration-tests-owlapi/pom.xml
@@ -5,7 +5,7 @@
cz.cvut.kbss.jopa
jopa-all
- 2.0.0
+ 2.0.1
../pom.xml
4.0.0
diff --git a/jopa-integration-tests-rdf4j/pom.xml b/jopa-integration-tests-rdf4j/pom.xml
index 986f1825a..4902b0e79 100644
--- a/jopa-integration-tests-rdf4j/pom.xml
+++ b/jopa-integration-tests-rdf4j/pom.xml
@@ -6,7 +6,7 @@
cz.cvut.kbss.jopa
jopa-all
- 2.0.0
+ 2.0.1
../pom.xml
jopa-integration-tests-rdf4j
diff --git a/jopa-integration-tests-rdf4j/src/test/java/cz/cvut/kbss/jopa/test/integration/rdf4j/UpdateOperationsTest.java b/jopa-integration-tests-rdf4j/src/test/java/cz/cvut/kbss/jopa/test/integration/rdf4j/UpdateOperationsTest.java
index b98ff219f..0ea708504 100644
--- a/jopa-integration-tests-rdf4j/src/test/java/cz/cvut/kbss/jopa/test/integration/rdf4j/UpdateOperationsTest.java
+++ b/jopa-integration-tests-rdf4j/src/test/java/cz/cvut/kbss/jopa/test/integration/rdf4j/UpdateOperationsTest.java
@@ -20,7 +20,6 @@
import cz.cvut.kbss.jopa.test.environment.Rdf4jDataAccessor;
import cz.cvut.kbss.jopa.test.environment.Rdf4jPersistenceFactory;
import cz.cvut.kbss.jopa.test.runner.UpdateOperationsRunner;
-import org.junit.jupiter.api.Disabled;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -31,11 +30,4 @@ public class UpdateOperationsTest extends UpdateOperationsRunner {
public UpdateOperationsTest() {
super(LOG, new Rdf4jPersistenceFactory(), new Rdf4jDataAccessor());
}
-
- // TODO Re-enable after new transactional mechanism is implemented
- @Disabled
- @Override
- public void concurrentTransactionsLeaveDataInConsistentState() {
- super.concurrentTransactionsLeaveDataInConsistentState();
- }
}
diff --git a/jopa-integration-tests/pom.xml b/jopa-integration-tests/pom.xml
index b38733e3a..408d25056 100644
--- a/jopa-integration-tests/pom.xml
+++ b/jopa-integration-tests/pom.xml
@@ -6,7 +6,7 @@
cz.cvut.kbss.jopa
jopa-all
- 2.0.0
+ 2.0.1
../pom.xml
4.0.0
diff --git a/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/OWLClassO.java b/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/OWLClassO.java
index cf253e749..3e57fe3b0 100644
--- a/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/OWLClassO.java
+++ b/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/OWLClassO.java
@@ -28,9 +28,12 @@ public class OWLClassO implements HasUri {
@Id
private URI uri;
- @OWLObjectProperty(iri = "http://krizik.felk.cvut.cz/ontologies/jopa/attributes#hasE", cascade = {CascadeType.MERGE}, fetch = FetchType.EAGER)
+ @OWLObjectProperty(iri = Vocabulary.P_O_SET_OF_E_ATTRIBUTE, cascade = {CascadeType.MERGE}, fetch = FetchType.EAGER)
private Set owlClassESet;
+ @OWLObjectProperty(iri = Vocabulary.P_O_SINGLE_E_ATTRIBUTE, cascade = CascadeType.ALL, fetch = FetchType.LAZY)
+ private OWLClassE owlClassE;
+
public OWLClassO() {
}
@@ -55,11 +58,20 @@ public void setOwlClassESet(Set owlClassESet) {
this.owlClassESet = owlClassESet;
}
+ public OWLClassE getOwlClassE() {
+ return owlClassE;
+ }
+
+ public void setOwlClassE(OWLClassE owlClassE) {
+ this.owlClassE = owlClassE;
+ }
+
@Override
public String toString() {
return "OWLClassO{" +
"uri=" + uri +
", owlClassESet=" + owlClassESet +
+ ", owlClassE=" + owlClassE +
'}';
}
}
diff --git a/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/OWLClassWithQueryAttr6.java b/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/OWLClassWithQueryAttr6.java
index 5efba93f3..d7fb7ece6 100644
--- a/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/OWLClassWithQueryAttr6.java
+++ b/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/OWLClassWithQueryAttr6.java
@@ -17,25 +17,31 @@
*/
package cz.cvut.kbss.jopa.test;
-import cz.cvut.kbss.jopa.model.annotations.*;
+import cz.cvut.kbss.jopa.model.annotations.CascadeType;
+import cz.cvut.kbss.jopa.model.annotations.FetchType;
+import cz.cvut.kbss.jopa.model.annotations.Id;
+import cz.cvut.kbss.jopa.model.annotations.OWLClass;
+import cz.cvut.kbss.jopa.model.annotations.OWLObjectProperty;
+import cz.cvut.kbss.jopa.model.annotations.Sparql;
import java.net.URI;
-import java.util.Set;
@OWLClass(iri = Vocabulary.C_OwlClassWithQueryAttr6)
public class OWLClassWithQueryAttr6 implements HasUri {
- private static final String QUERY = "SELECT ?pluralAttribute\n" +
- "WHERE {?this <" + Vocabulary.P_HAS_SIMPLE_LIST + "> ?pluralAttribute}";
+ private static final String QUERY = "SELECT ?d WHERE { " +
+ "?d a <" + Vocabulary.C_OWL_CLASS_D + "> ;" +
+ " <" + Vocabulary.P_HAS_OWL_CLASS_A + "> ?a . " +
+ "?this <" + Vocabulary.P_HAS_OWL_CLASS_A + "> ?a . }";
@Id
private URI uri;
- @OWLObjectProperty(iri = Vocabulary.P_HAS_SIMPLE_LIST, cascade = CascadeType.ALL)
- private Set pluralAttribute;
+ @OWLObjectProperty(iri = Vocabulary.P_HAS_OWL_CLASS_A, cascade = CascadeType.ALL)
+ private OWLClassA owlClassA;
- @Sparql(query=QUERY, fetchType = FetchType.LAZY)
- private Set pluralQueryAttribute;
+ @Sparql(query = QUERY, fetchType = FetchType.LAZY)
+ private OWLClassD lazyQueryAttribute;
public OWLClassWithQueryAttr6() {
}
@@ -53,30 +59,26 @@ public URI getUri() {
return uri;
}
- public Set getPluralAttribute() {
- return pluralAttribute;
+ public OWLClassA getOwlClassA() {
+ return owlClassA;
}
- public void setPluralAttribute(Set pluralAttribute) {
- this.pluralAttribute = pluralAttribute;
+ public void setOwlClassA(OWLClassA owlClassA) {
+ this.owlClassA = owlClassA;
}
- public Set getPluralQueryAttribute() {
- return pluralQueryAttribute;
+ public OWLClassD getLazyQueryAttribute() {
+ return lazyQueryAttribute;
}
- public void setPluralQueryAttribute(Set pluralQueryAttribute) {
- this.pluralQueryAttribute = pluralQueryAttribute;
- }
-
- public static String getSparqlQuery() {
- return QUERY;
+ public void setLazyQueryAttribute(OWLClassD lazyQueryAttribute) {
+ this.lazyQueryAttribute = lazyQueryAttribute;
}
@Override
public String toString() {
String out = "OWLClassWithQueryAttr: uri = " + uri;
- out += ", pluralAttribute = " + pluralAttribute;
+ out += ", owlClassA = " + owlClassA;
return out;
}
}
diff --git a/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/Vocabulary.java b/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/Vocabulary.java
index 1043e020d..b7fa1214d 100644
--- a/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/Vocabulary.java
+++ b/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/Vocabulary.java
@@ -110,6 +110,9 @@ public class Vocabulary {
public static final String P_N_URI_ANNOTATION_PROPERTY = ATTRIBUTE_IRI_BASE + "annotationUri";
public static final String P_N_STRING_ATTRIBUTE = ATTRIBUTE_IRI_BASE + "N-stringAttribute";
+ public static final String P_O_SET_OF_E_ATTRIBUTE = ATTRIBUTE_IRI_BASE + "hasESet";
+ public static final String P_O_SINGLE_E_ATTRIBUTE = ATTRIBUTE_IRI_BASE + "hasE";
+
public static final String P_A_STRING_ATTRIBUTE = ATTRIBUTE_IRI_BASE + "A-stringAttribute";
public static final String P_B_STRING_ATTRIBUTE = ATTRIBUTE_IRI_BASE + "B-stringAttribute";
public static final String P_E_STRING_ATTRIBUTE = ATTRIBUTE_IRI_BASE + "E-stringAttribute";
diff --git a/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/environment/Generators.java b/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/environment/Generators.java
index c38be057e..a4e70a226 100644
--- a/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/environment/Generators.java
+++ b/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/environment/Generators.java
@@ -155,31 +155,26 @@ public static Map> createTypedProperties(int size) {
private static Object generateRandomPropertyValue(int valueIndex, int propertyIndex) {
final int random = randomInt(10);
- switch (random) {
- case 0: // boolean
- return valueIndex % 2 == 0;
- case 1: // int
- return valueIndex;
- case 2: // long
- return System.currentTimeMillis();
- case 3: //double
- return ((double) propertyIndex + 1) / (valueIndex + 1);
- case 4: // datetime
+ return switch (random) {
+ case 0 -> // boolean
+ valueIndex % 2 == 0;
+ case 1 -> // int
+ valueIndex;
+ case 2 -> // long
+ System.currentTimeMillis();
+ case 3 -> //double
+ ((double) propertyIndex + 1) / (valueIndex + 1);
+ case 4 -> // datetime
// Generate date rounded to milliseconds to prevent issues with time rounding
- return OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS);
- case 5:
- return OffsetTime.now().truncatedTo(ChronoUnit.MILLIS);
- case 6:
- return LocalDate.now();
- case 7: // String
- return "TypedProperty_" + propertyIndex + "Value_" + valueIndex;
- case 8:
- return BigInteger.valueOf(valueIndex);
- case 9:
- return BigDecimal.valueOf(Math.PI);
- default:
- throw new IllegalArgumentException();
- }
+ OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS);
+ case 5 -> OffsetTime.now().truncatedTo(ChronoUnit.MILLIS);
+ case 6 -> LocalDate.now();
+ case 7 -> // String
+ "TypedProperty_" + propertyIndex + "Value_" + valueIndex;
+ case 8 -> BigInteger.valueOf(valueIndex);
+ case 9 -> BigDecimal.valueOf(Math.PI);
+ default -> throw new IllegalArgumentException();
+ };
}
private static void generateInstances(Collection col, String uriBase, int size) {
@@ -195,6 +190,13 @@ private static void generateInstances(Collection col, String uriBase,
}
}
+ public static OWLClassA generateOwlClassA() {
+ final OWLClassA a = new OWLClassA(generateUri());
+ a.setStringAttribute("String attribute " + randomInt(10000));
+ a.setTypes(TYPES);
+ return a;
+ }
+
private static Set getTypes() {
final Set types = new HashSet<>(3);
types.add(Vocabulary.CLASS_IRI_BASE + "OWLClassDF");
diff --git a/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/query/runner/TypedQueryRunner.java b/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/query/runner/TypedQueryRunner.java
index 630770095..25607fcc3 100644
--- a/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/query/runner/TypedQueryRunner.java
+++ b/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/query/runner/TypedQueryRunner.java
@@ -23,10 +23,12 @@
import cz.cvut.kbss.jopa.model.descriptors.Descriptor;
import cz.cvut.kbss.jopa.model.descriptors.EntityDescriptor;
import cz.cvut.kbss.jopa.model.query.TypedQuery;
+import cz.cvut.kbss.jopa.query.QueryHints;
import cz.cvut.kbss.jopa.test.*;
import cz.cvut.kbss.jopa.test.environment.DataAccessor;
import cz.cvut.kbss.jopa.test.environment.Generators;
import cz.cvut.kbss.jopa.test.query.QueryTestEnvironment;
+import cz.cvut.kbss.ontodriver.Statement;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
@@ -211,6 +213,7 @@ public void askQueryAgainstTransactionalOntologyContainsUncommittedChangesAsWell
"ASK { ?individual a ?type . }",
Boolean.class).setParameter("individual", e.getUri()).setParameter("type",
URI.create(Vocabulary.C_OWL_CLASS_E));
+ query.setHint(QueryHints.TARGET_ONTOLOGY, Statement.StatementOntology.TRANSACTIONAL.toString());
final Boolean res = query.getSingleResult();
assertTrue(res);
} finally {
diff --git a/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/runner/RetrieveOperationsRunner.java b/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/runner/RetrieveOperationsRunner.java
index 6e225aa26..b720fd59a 100644
--- a/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/runner/RetrieveOperationsRunner.java
+++ b/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/runner/RetrieveOperationsRunner.java
@@ -46,7 +46,6 @@
import cz.cvut.kbss.jopa.vocabulary.RDF;
import cz.cvut.kbss.ontodriver.ReloadableDataSource;
import cz.cvut.kbss.ontodriver.config.OntoDriverProperties;
-import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
@@ -385,9 +384,7 @@ private static void replaceFileContents(File target) throws IOException {
protected abstract void addFileStorageProperties(Map properties);
@Test
- @Disabled
- void getReferenceRetrievesReferenceToInstanceWithDataPropertiesWhoseAttributesAreLoadedLazily()
- throws Exception {
+ void getReferenceRetrievesReferenceToInstanceWithDataPropertiesWhoseAttributesAreLoadedLazily() throws Exception {
this.em = getEntityManager(
"getReferenceRetrievesReferenceToInstanceWithDataPropertiesWhoseAttributesAreLoadedLazily", false);
persist(entityM);
@@ -512,27 +509,24 @@ void testRetrieveEntityWithManagedTypeQueryAttr() {
}
@Test
- @Disabled
void testRetrieveWithLazyQueryAttribute() throws Exception {
this.em = getEntityManager("RetrieveLazyQueryAttr", false);
- Set simpleSet = Generators.createSimpleSet(20);
- entityWithQueryAttr6.setPluralAttribute(simpleSet);
+ entityWithQueryAttr6.setOwlClassA(entityA);
+ entityD.setOwlClassA(entityA);
+
- persist(entityWithQueryAttr6);
+ persist(entityWithQueryAttr6, entityD);
final OWLClassWithQueryAttr6 res = findRequired(OWLClassWithQueryAttr6.class, entityWithQueryAttr6.getUri());
- final Field f = OWLClassWithQueryAttr6.class.getDeclaredField("pluralQueryAttribute");
+ final Field f = OWLClassWithQueryAttr6.class.getDeclaredField("lazyQueryAttribute");
f.setAccessible(true);
Object value = f.get(res);
assertInstanceOf(LazyLoadingProxy.class, value);
- assertNotNull(res.getPluralQueryAttribute());
+ assertNotNull(res.getLazyQueryAttribute());
value = f.get(res);
assertNotNull(value);
- assertEquals(entityWithQueryAttr6.getPluralAttribute(), res.getPluralQueryAttribute());
- for (OWLClassA classA : res.getPluralQueryAttribute()) {
- assertTrue(em.contains(classA));
- }
+ assertEquals(entityD.getUri(), res.getLazyQueryAttribute().getUri());
}
@Test
diff --git a/jopa-integration-tests/src/test/java/cz/cvut/kbss/jopa/test/integration/BugTest.java b/jopa-integration-tests/src/test/java/cz/cvut/kbss/jopa/test/integration/BugTest.java
index 89c526350..8e63535ff 100644
--- a/jopa-integration-tests/src/test/java/cz/cvut/kbss/jopa/test/integration/BugTest.java
+++ b/jopa-integration-tests/src/test/java/cz/cvut/kbss/jopa/test/integration/BugTest.java
@@ -26,14 +26,17 @@
import cz.cvut.kbss.jopa.oom.exception.UnpersistedChangeException;
import cz.cvut.kbss.jopa.test.OWLClassA;
import cz.cvut.kbss.jopa.test.OWLClassD;
+import cz.cvut.kbss.jopa.test.OWLClassE;
import cz.cvut.kbss.jopa.test.OWLClassF;
import cz.cvut.kbss.jopa.test.OWLClassJ;
+import cz.cvut.kbss.jopa.test.OWLClassO;
import cz.cvut.kbss.jopa.test.OWLClassR;
import cz.cvut.kbss.jopa.test.Vocabulary;
import cz.cvut.kbss.jopa.test.environment.Generators;
import cz.cvut.kbss.jopa.utils.JOPALazyUtils;
import cz.cvut.kbss.jopa.vocabulary.RDFS;
import cz.cvut.kbss.ontodriver.descriptor.AxiomDescriptor;
+import cz.cvut.kbss.ontodriver.descriptor.AxiomValueDescriptor;
import cz.cvut.kbss.ontodriver.exception.OntoDriverException;
import cz.cvut.kbss.ontodriver.model.Assertion;
import cz.cvut.kbss.ontodriver.model.Axiom;
@@ -62,6 +65,8 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
@@ -230,7 +235,9 @@ private void initOWLClassFAxioms(OWLClassF instance) throws Exception {
new Value<>(NamedResource.create(Vocabulary.C_OWL_CLASS_F)));
final Assertion aSetAssertion = Assertion.createObjectPropertyAssertion(URI.create(Vocabulary.P_F_HAS_SIMPLE_SET), false);
axioms.add(classAssertion);
- final List> aSetAxioms = instance.getSimpleSet().stream().map(a -> new AxiomImpl<>(subject, aSetAssertion, new Value<>(NamedResource.create(a.getUri())))).toList();
+ final List> aSetAxioms = instance.getSimpleSet().stream()
+ .map(a -> new AxiomImpl<>(subject, aSetAssertion, new Value<>(NamedResource.create(a.getUri()))))
+ .toList();
axioms.addAll(aSetAxioms);
final AxiomDescriptor entityDesc = new AxiomDescriptor(subject);
entityDesc.addAssertion(Assertion.createClassAssertion(false));
@@ -242,4 +249,33 @@ private void initOWLClassFAxioms(OWLClassF instance) throws Exception {
setDesc.addAssertion(aSetAssertion);
doReturn(aSetAxioms).when(connectionMock).find(setDesc);
}
+
+ /**
+ * Bug #248
+ */
+ @Test
+ void cascadeMergeOnLazyLoadingProxyDoesNothing() throws Exception {
+ final OWLClassO owner = new OWLClassO(Generators.generateUri());
+ final OWLClassE reference = new OWLClassE();
+ reference.setUri(Generators.generateUri());
+ owner.setOwlClassE(reference);
+ final Assertion classAssertion = Assertion.createClassAssertion(false);
+ final Assertion eAssertion = Assertion.createObjectPropertyAssertion(URI.create(Vocabulary.P_O_SINGLE_E_ATTRIBUTE), false);
+ final Assertion eSetAssertion = Assertion.createObjectPropertyAssertion(URI.create(Vocabulary.P_O_SET_OF_E_ATTRIBUTE), false);
+ final NamedResource ownerSubject = NamedResource.create(owner.getUri());
+ final AxiomDescriptor ownerDesc = new AxiomDescriptor(ownerSubject);
+ ownerDesc.addAssertion(classAssertion);
+ ownerDesc.addAssertion(eAssertion);
+ ownerDesc.addAssertion(eSetAssertion);
+ final List> ownerAxioms = List.of(
+ new AxiomImpl<>(NamedResource.create(owner.getUri()), classAssertion, new Value<>(URI.create(Vocabulary.C_OWL_CLASS_O))),
+ new AxiomImpl<>(NamedResource.create(owner.getUri()), eAssertion, new Value<>(NamedResource.create(reference.getUri())))
+ );
+ when(connectionMock.find(ownerDesc)).thenReturn(ownerAxioms);
+
+ em.getTransaction().begin();
+ final OWLClassO subject = em.find(OWLClassO.class, owner.getUri());
+ em.merge(subject);
+ verify(connectionMock, never()).update(any(AxiomValueDescriptor.class));
+ }
}
diff --git a/jopa-integration-tests/src/test/java/cz/cvut/kbss/jopa/test/integration/EntityManagerTest.java b/jopa-integration-tests/src/test/java/cz/cvut/kbss/jopa/test/integration/EntityManagerTest.java
index 8b0095de4..3d53b7895 100644
--- a/jopa-integration-tests/src/test/java/cz/cvut/kbss/jopa/test/integration/EntityManagerTest.java
+++ b/jopa-integration-tests/src/test/java/cz/cvut/kbss/jopa/test/integration/EntityManagerTest.java
@@ -18,6 +18,7 @@
package cz.cvut.kbss.jopa.test.integration;
import cz.cvut.kbss.jopa.model.JOPAPersistenceProperties;
+import cz.cvut.kbss.jopa.test.OWLClassA;
import cz.cvut.kbss.jopa.test.OWLClassF;
import cz.cvut.kbss.jopa.test.Vocabulary;
import cz.cvut.kbss.jopa.test.environment.Generators;
@@ -30,6 +31,7 @@
import cz.cvut.kbss.ontodriver.model.Value;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.junit.jupiter.MockitoExtension;
import java.net.URI;
@@ -78,4 +80,17 @@ void mergeWithNullInferredValueIgnoresChangeWhenIgnoreInferredValueRemovalsOnMer
verify(connectionMock, never()).update(any(AxiomValueDescriptor.class));
assertEquals(inferredValue, result.getSecondStringAttribute());
}
+
+ @Test
+ void flushWritesChangesToRepository() throws Exception {
+ final OWLClassA entity = new OWLClassA(Generators.generateUri());
+ entity.setStringAttribute("Test string");
+ em.getTransaction().begin();
+ em.persist(entity);
+ em.flush();
+ final ArgumentCaptor captor = ArgumentCaptor.forClass(AxiomValueDescriptor.class);
+ verify(connectionMock).persist(captor.capture());
+ assertEquals(entity.getUri(), captor.getValue().getSubject().getIdentifier());
+ em.getTransaction().commit();
+ }
}
diff --git a/jopa-maven-plugin/pom.xml b/jopa-maven-plugin/pom.xml
index 2f2267465..a600e75b2 100644
--- a/jopa-maven-plugin/pom.xml
+++ b/jopa-maven-plugin/pom.xml
@@ -5,7 +5,7 @@
jopa-all
cz.cvut.kbss.jopa
- 2.0.0
+ 2.0.1
../pom.xml
4.0.0
diff --git a/jopa-owl2java/pom.xml b/jopa-owl2java/pom.xml
index adcbf84e6..7fc94244c 100644
--- a/jopa-owl2java/pom.xml
+++ b/jopa-owl2java/pom.xml
@@ -6,7 +6,7 @@
cz.cvut.kbss.jopa
jopa-all
- 2.0.0
+ 2.0.1
../pom.xml
@@ -62,46 +62,24 @@
-
-
- release
-
-
-
-
- com.google.code.maven-replacer-plugin
- replacer
- 1.5.3
-
-
- prepare-package
-
- replace
-
-
-
-
- false
- ${project.basedir}/src/main/java/cz/cvut/kbss/jopa/owl2java/Constants.java
- false
- $VERSION$
- ${project.parent.version}
-
-
-
-
-
-
-
src/main/sh
true
+
+ src/main/resources
+ true
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+ ${maven.resources.plugin.version}
+
org.apache.maven.plugins
maven-jar-plugin
diff --git a/jopa-owl2java/src/main/java/cz/cvut/kbss/jopa/owl2java/Constants.java b/jopa-owl2java/src/main/java/cz/cvut/kbss/jopa/owl2java/Constants.java
index 66a13d2d8..4f679fdd6 100644
--- a/jopa-owl2java/src/main/java/cz/cvut/kbss/jopa/owl2java/Constants.java
+++ b/jopa-owl2java/src/main/java/cz/cvut/kbss/jopa/owl2java/Constants.java
@@ -17,8 +17,12 @@
*/
package cz.cvut.kbss.jopa.owl2java;
+import cz.cvut.kbss.jopa.owl2java.exception.OWL2JavaException;
+
+import java.io.IOException;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
+import java.util.Properties;
public class Constants {
@@ -86,10 +90,21 @@ public class Constants {
/**
* Tool version.
*/
- public static final String VERSION = "$VERSION$";
+ public static final String VERSION = resolveVersion();
private Constants() {
throw new AssertionError();
}
+
+ private static String resolveVersion() {
+ final Properties properties = new Properties();
+ try {
+ properties.load(Constants.class.getClassLoader().getResourceAsStream("owl2java.properties"));
+ assert properties.containsKey("cz.cvut.jopa.owl2java.version");
+ return properties.getProperty("cz.cvut.jopa.owl2java.version");
+ } catch (IOException e) {
+ throw new OWL2JavaException("Unable to load OWL2Java version from properties file.");
+ }
+ }
}
diff --git a/jopa-owl2java/src/main/resources/owl2java.properties b/jopa-owl2java/src/main/resources/owl2java.properties
new file mode 100644
index 000000000..ddf5e3f61
--- /dev/null
+++ b/jopa-owl2java/src/main/resources/owl2java.properties
@@ -0,0 +1 @@
+cz.cvut.jopa.owl2java.version=${project.version}
diff --git a/jopa-owlapi-utils/pom.xml b/jopa-owlapi-utils/pom.xml
index 120223ac4..915dbe47e 100644
--- a/jopa-owlapi-utils/pom.xml
+++ b/jopa-owlapi-utils/pom.xml
@@ -6,7 +6,7 @@
cz.cvut.kbss.jopa
jopa-all
- 2.0.0
+ 2.0.1
../pom.xml
diff --git a/modelgen/pom.xml b/modelgen/pom.xml
index 659187942..7934fe457 100644
--- a/modelgen/pom.xml
+++ b/modelgen/pom.xml
@@ -6,7 +6,7 @@
jopa-all
cz.cvut.kbss.jopa
- 2.0.0
+ 2.0.1
../pom.xml
diff --git a/ontodriver-api/pom.xml b/ontodriver-api/pom.xml
index e76d87cad..d05ba7032 100644
--- a/ontodriver-api/pom.xml
+++ b/ontodriver-api/pom.xml
@@ -6,7 +6,7 @@
jopa-all
cz.cvut.kbss.jopa
- 2.0.0
+ 2.0.1
../pom.xml
diff --git a/ontodriver-api/src/main/java/cz/cvut/kbss/ontodriver/descriptor/ReferencedListDescriptorImpl.java b/ontodriver-api/src/main/java/cz/cvut/kbss/ontodriver/descriptor/ReferencedListDescriptorImpl.java
index 0f1f9b44c..4d85ae4aa 100644
--- a/ontodriver-api/src/main/java/cz/cvut/kbss/ontodriver/descriptor/ReferencedListDescriptorImpl.java
+++ b/ontodriver-api/src/main/java/cz/cvut/kbss/ontodriver/descriptor/ReferencedListDescriptorImpl.java
@@ -97,9 +97,15 @@ public int hashCode() {
@Override
public boolean equals(Object obj) {
- if (this == obj) {return true;}
- if (obj == null) {return false;}
- if (getClass() != obj.getClass()) {return false;}
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
ReferencedListDescriptorImpl other = (ReferencedListDescriptorImpl) obj;
return descriptor.equals(other.descriptor) && nodeContent.equals(other.nodeContent) && terminatedByNil == other.terminatedByNil;
}
diff --git a/ontodriver-api/src/main/java/cz/cvut/kbss/ontodriver/descriptor/ReferencedListValueDescriptor.java b/ontodriver-api/src/main/java/cz/cvut/kbss/ontodriver/descriptor/ReferencedListValueDescriptor.java
index 002eef180..adb02811e 100644
--- a/ontodriver-api/src/main/java/cz/cvut/kbss/ontodriver/descriptor/ReferencedListValueDescriptor.java
+++ b/ontodriver-api/src/main/java/cz/cvut/kbss/ontodriver/descriptor/ReferencedListValueDescriptor.java
@@ -65,11 +65,19 @@ public int hashCode() {
@Override
public boolean equals(Object obj) {
- if (this == obj) {return true;}
- if (obj == null || getClass() != obj.getClass()) {return false;}
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
ReferencedListValueDescriptor> other = (ReferencedListValueDescriptor>) obj;
- if (!descriptor.equals(other.descriptor)) {return false;}
- if (!getNodeContent().equals(other.getNodeContent())) {return false;}
+ if (!descriptor.equals(other.descriptor)) {
+ return false;
+ }
+ if (!getNodeContent().equals(other.getNodeContent())) {
+ return false;
+ }
return values.equals(other.values);
}
diff --git a/ontodriver-jena/pom.xml b/ontodriver-jena/pom.xml
index c07fc74bb..38d8748ce 100644
--- a/ontodriver-jena/pom.xml
+++ b/ontodriver-jena/pom.xml
@@ -8,7 +8,7 @@
cz.cvut.kbss.jopa
jopa-all
- 2.0.0
+ 2.0.1
../pom.xml
diff --git a/ontodriver-jena/src/main/java/cz/cvut/kbss/ontodriver/jena/list/ReferencedListHandler.java b/ontodriver-jena/src/main/java/cz/cvut/kbss/ontodriver/jena/list/ReferencedListHandler.java
index f65e6a7ff..1fcf7d263 100644
--- a/ontodriver-jena/src/main/java/cz/cvut/kbss/ontodriver/jena/list/ReferencedListHandler.java
+++ b/ontodriver-jena/src/main/java/cz/cvut/kbss/ontodriver/jena/list/ReferencedListHandler.java
@@ -107,7 +107,7 @@ private Resource generateNewListNode(URI baseUri, String context, int index) {
return node;
}
- private Optional createNilTerminal(Resource lastNode, Property hasNext,
+ private static Optional createNilTerminal(Resource lastNode, Property hasNext,
ReferencedListDescriptor descriptor) {
return descriptor.isTerminatedByNil() ? Optional.of(createStatement(lastNode, hasNext, RDF.nil)) : Optional.empty();
}
diff --git a/ontodriver-owlapi/pom.xml b/ontodriver-owlapi/pom.xml
index 9e71fcb2b..86e92bb2e 100644
--- a/ontodriver-owlapi/pom.xml
+++ b/ontodriver-owlapi/pom.xml
@@ -11,7 +11,7 @@
cz.cvut.kbss.jopa
jopa-all
- 2.0.0
+ 2.0.1
../pom.xml
diff --git a/ontodriver-rdf4j/pom.xml b/ontodriver-rdf4j/pom.xml
index 2953faf81..cc8909e78 100644
--- a/ontodriver-rdf4j/pom.xml
+++ b/ontodriver-rdf4j/pom.xml
@@ -10,11 +10,11 @@
jopa-all
cz.cvut.kbss.jopa
- 2.0.0
+ 2.0.1
- 4.3.11
+ 4.3.12
2.0.9
diff --git a/pom.xml b/pom.xml
index 7071bfa72..468d3cc64 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
4.0.0
cz.cvut.kbss.jopa
- 2.0.0
+ 2.0.1
jopa-all
pom
JOPA
@@ -37,6 +37,7 @@
17
${java.version}
${java.version}
+ yyyy-MM-dd
2.0.6
5.9.2
@@ -49,6 +50,7 @@
3.1.1
3.6.3
3.2.5
+ 3.3.1