diff --git a/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/PersistenceUnitUtil.java b/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/PersistenceUnitUtil.java
index 64aff75c2..51cda429c 100644
--- a/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/PersistenceUnitUtil.java
+++ b/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/PersistenceUnitUtil.java
@@ -1,35 +1,55 @@
/**
* Copyright (C) 2016 Czech Technical University in Prague
- *
- * This program is free software: you can redistribute it and/or modify it under
- * the terms of the GNU General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any
- * later version.
- *
- * This program 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 General Public License for more
- * details. You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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 General Public License for more
+ * details. You should have received a copy of the GNU General Public License along with this program. If not, see
+ * .
*/
package cz.cvut.kbss.jopa.model;
+/**
+ * Utility interface between the application and the persistence provider managing the persistence unit.
+ *
+ * The methods of this interface should only be invoked on entity instances obtained from or managed by entity managers
+ * for this persistence unit or on new entity instances.
+ */
public interface PersistenceUnitUtil {
- public boolean isLoaded(Object entity, String attributeName);
+ /**
+ * Determine the load state of a given persistent attribute of an entity belonging to the persistence unit.
+ *
+ * @param entity entity instance containing the attribute
+ * @param attributeName name of attribute whose load state is to be determined
+ * @return false if entity's state has not been loaded or if the attribute state has not been loaded, else true
+ */
+ boolean isLoaded(Object entity, String attributeName);
- public boolean isLoaded(Object entity);
+ /**
+ * Determine the load state of an entity belonging to the persistence unit.
+ *
+ * This method can be used to determine the load state of an entity passed as a reference. An entity is considered
+ * loaded if all attributes for which {@code FetchType.EAGER} has been specified have been loaded.
+ *
+ * The {@link #isLoaded(Object, String)} method should be used to determine the load state of an attribute. Not
+ * doing so might lead to unintended loading of state.
+ *
+ * @param entity entity instance whose load state is to be determined
+ * @return false if the entity has not been loaded, else true
+ */
+ boolean isLoaded(Object entity);
- /**
- * Return the id of the entity. A generated id is not guaranteed to be
- * available until after the database insert has occurred. Returns null if
- * the entity does not yet have an id.
- *
- * @param entity
- * entity instance
- * @return id of the entity
- * @throws IllegalArgumentException
- * if the object is found not to be an entity
- */
- public Object getIdentifier(Object entity);
+ /**
+ * Return the id of the entity. A generated id is not guaranteed to be available until after the database insert has
+ * occurred. Returns null if the entity does not yet have an id.
+ *
+ * @param entity entity instance
+ * @return id of the entity
+ * @throws IllegalArgumentException if the object is found not to be an entity
+ */
+ Object getIdentifier(Object entity);
}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/EntityManagerFactoryImpl.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/EntityManagerFactoryImpl.java
index a9145b462..5a6d48acf 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/EntityManagerFactoryImpl.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/EntityManagerFactoryImpl.java
@@ -1,16 +1,14 @@
/**
* Copyright (C) 2016 Czech Technical University in Prague
- *
- * This program is free software: you can redistribute it and/or modify it under
- * the terms of the GNU General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any
- * later version.
- *
- * This program 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 General Public License for more
- * details. You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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 General Public License for more
+ * details. You should have received a copy of the GNU General Public License along with this program. If not, see
+ * .
*/
package cz.cvut.kbss.jopa.model;
@@ -25,10 +23,7 @@
import cz.cvut.kbss.ontodriver.OntologyStorageProperties;
import cz.cvut.kbss.ontodriver.config.OntoDriverProperties;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class EntityManagerFactoryImpl implements EntityManagerFactory, PersistenceUnitUtil {
@@ -160,16 +155,18 @@ public void addNamedQuery(String name, Query query) {
@Override
public Object getIdentifier(Object entity) {
+ Objects.requireNonNull(entity);
final EntityType> et = getMetamodel().entity(entity.getClass());
return EntityPropertiesUtils.getFieldValue(et.getIdentifier().getJavaField(), entity);
}
@Override
public boolean isLoaded(Object entity, String attributeName) {
+ Objects.requireNonNull(entity);
+ Objects.requireNonNull(attributeName);
for (final AbstractEntityManager emi : em) {
- if (emi.contains(entity)) {
- return attributeName == null || emi.isLoaded(entity, attributeName);
-
+ if (emi.contains(entity) && emi.isLoaded(entity, attributeName)) {
+ return true;
}
}
@@ -178,9 +175,14 @@ public boolean isLoaded(Object entity, String attributeName) {
@Override
public boolean isLoaded(Object entity) {
- // TODO
+ Objects.requireNonNull(entity);
+ // Since we do not support getReference yet, all EAGER attributes are always loaded for managed instances
+ for (AbstractEntityManager emi : em) {
+ if (emi.contains(entity)) {
+ return true;
+ }
+ }
return false;
- // return isLoaded(entity);
}
@Override
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 d8deb30d6..5e35fcf09 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
@@ -1,25 +1,25 @@
/**
* Copyright (C) 2016 Czech Technical University in Prague
- *
- * This program is free software: you can redistribute it and/or modify it under
- * the terms of the GNU General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any
- * later version.
- *
- * This program 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 General Public License for more
- * details. You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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 General Public License for more
+ * details. You should have received a copy of the GNU General Public License along with this program. If not, see
+ * .
*/
package cz.cvut.kbss.jopa.model;
import cz.cvut.kbss.jopa.exceptions.OWLPersistenceException;
import cz.cvut.kbss.jopa.exceptions.TransactionRequiredException;
import cz.cvut.kbss.jopa.model.annotations.CascadeType;
+import cz.cvut.kbss.jopa.model.annotations.FetchType;
import cz.cvut.kbss.jopa.model.descriptors.Descriptor;
import cz.cvut.kbss.jopa.model.descriptors.EntityDescriptor;
import cz.cvut.kbss.jopa.model.metamodel.Attribute;
+import cz.cvut.kbss.jopa.model.metamodel.FieldSpecification;
import cz.cvut.kbss.jopa.model.metamodel.Metamodel;
import cz.cvut.kbss.jopa.model.query.Query;
import cz.cvut.kbss.jopa.model.query.TypedQuery;
@@ -409,8 +409,17 @@ public Metamodel getMetamodel() {
@Override
public boolean isLoaded(final Object object, final String attributeName) {
- // TODO
- return false;
+ Objects.requireNonNull(object);
+ Objects.requireNonNull(attributeName);
+ if (!contains(object)) {
+ return false;
+ }
+ final FieldSpecification, ?> fieldSpec = getMetamodel().entity(object.getClass())
+ .getFieldSpecification(attributeName);
+ // This is not correct, as lazily loaded fields can be set to null, but as long as we do not have any representation
+ // of the loaded state of an entity, this will have to do
+ return fieldSpec.getFetchType() == FetchType.EAGER ||
+ EntityPropertiesUtils.getFieldValue(fieldSpec.getJavaField(), object) != null;
}
@Override
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 f72cbe240..2158f1c15 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
@@ -62,6 +62,7 @@ public static void initOWLClassAMocks(EntityTypeImpl etMock, Attribut
when(strAttMock.getPersistentAttributeType()).thenReturn(Attribute.PersistentAttributeType.DATA);
when(strAttMock.getConstraints()).thenReturn(new ParticipationConstraint[]{});
when(strAttMock.getCascadeTypes()).thenReturn(new CascadeType[0]);
+ when(strAttMock.getFetchType()).thenReturn(FetchType.EAGER);
when(typesMock.getJavaField()).thenReturn(OWLClassA.getTypesField());
when(typesMock.getName()).thenReturn(OWLClassA.getTypesField().getName());
when(typesMock.getDeclaringType()).thenReturn(etMock);
@@ -387,6 +388,9 @@ public static void initOWLClassKMocks(EntityTypeImpl etMock, Attribut
when(clsEMock.getIRI()).thenReturn(IRI.create(clsEIri));
when(clsEMock.getJavaType()).thenReturn(OWLClassE.class);
when(clsEMock.getDeclaringType()).thenReturn(etMock);
+ when(clsEMock.getFetchType()).thenReturn(FetchType.LAZY);
+ when(clsEMock.getName()).thenReturn(OWLClassK.getOwlClassEField().getName());
+ when(etMock.getFieldSpecification(clsEMock.getName())).thenReturn(clsEMock);
when(etMock.getIdentifier()).thenReturn(idMock);
when(idMock.getJavaField()).thenReturn(OWLClassK.class.getDeclaredField("uri"));
when(etMock.getLifecycleListenerManager()).thenReturn(EntityLifecycleListenerManager.empty());
diff --git a/jopa-impl/src/test/java/cz/cvut/kbss/jopa/model/EntityManagerFactoryImplTest.java b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/model/EntityManagerFactoryImplTest.java
new file mode 100644
index 000000000..658a91a7e
--- /dev/null
+++ b/jopa-impl/src/test/java/cz/cvut/kbss/jopa/model/EntityManagerFactoryImplTest.java
@@ -0,0 +1,89 @@
+package cz.cvut.kbss.jopa.model;
+
+import cz.cvut.kbss.jopa.environment.OWLClassA;
+import cz.cvut.kbss.jopa.environment.utils.DataSourceStub;
+import cz.cvut.kbss.jopa.environment.utils.Generators;
+import cz.cvut.kbss.ontodriver.Connection;
+import cz.cvut.kbss.ontodriver.Types;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class EntityManagerFactoryImplTest {
+
+ private EntityManagerFactoryImpl emf;
+
+ @Mock
+ private Connection connection;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ final Map props = new HashMap<>();
+ props.put(JOPAPersistenceProperties.DATA_SOURCE_CLASS, DataSourceStub.class.getName());
+ props.put(JOPAPersistenceProperties.ONTOLOGY_PHYSICAL_URI_KEY,
+ Generators.createIndividualIdentifier().toString());
+ props.put(JOPAPersistenceProperties.SCAN_PACKAGE, "cz.cvut.kbss.jopa.environment");
+ this.emf = new EntityManagerFactoryImpl(props);
+ emf.createEntityManager();
+ emf.getServerSession().unwrap(DataSourceStub.class).setConnection(connection);
+ when(connection.types()).thenReturn(mock(Types.class));
+ }
+
+ @Test
+ public void isLoadedReturnsTrueForManagedInstance() {
+ final EntityManager em = emf.createEntityManager();
+ try {
+ final OWLClassA a = Generators.generateOwlClassAInstance();
+ em.persist(a);
+ assertTrue(emf.isLoaded(a));
+ } finally {
+ em.close();
+ }
+ }
+
+ @Test
+ public void isLoadedReturnsTrueForAttributeOfManagedInstance() throws Exception {
+ final EntityManager em = emf.createEntityManager();
+ try {
+ final OWLClassA a = Generators.generateOwlClassAInstance();
+ em.persist(a);
+ assertTrue(emf.isLoaded(a, OWLClassA.getStrAttField().getName()));
+ } finally {
+ em.close();
+ }
+ }
+
+ @Test
+ public void isLoadedReturnsFalseNonNonManagedInstance() {
+ final EntityManager emOne = emf.createEntityManager();
+ final EntityManager emTwo = emf.createEntityManager();
+ try {
+ assertFalse(emf.isLoaded(Generators.generateOwlClassAInstance()));
+ } finally {
+ emOne.close();
+ emTwo.close();
+ }
+ }
+
+ @Test
+ public void isLoadedReturnsFalseNonNonManagedInstanceWithAttribute() throws Exception {
+ final EntityManager emOne = emf.createEntityManager();
+ final EntityManager emTwo = emf.createEntityManager();
+ try {
+ assertFalse(emf.isLoaded(Generators.generateOwlClassAInstance(), OWLClassA.getStrAttField().getName()));
+ } finally {
+ emOne.close();
+ emTwo.close();
+ }
+ }
+}
\ No newline at end of file
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 31116f8ba..395883491 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
@@ -481,4 +481,31 @@ public void removeIsAbleToBreakCascadingCycle() throws Exception {
verify(uow).removeObject(cloneOne);
verify(uow).removeObject(cloneTwo);
}
+
+ @Test
+ public void isLoadedReturnsTrueForEagerlyLoadedAttributeOfManagedInstance() throws Exception {
+ final OWLClassA a = Generators.generateOwlClassAInstance();
+ doAnswer((invocationOnMock) -> a).when(uow).readObject(eq(OWLClassA.class), eq(a.getUri()), any(Descriptor.class));
+ when(uow.contains(a)).thenReturn(true);
+ final OWLClassA found = em.find(OWLClassA.class, a.getUri());
+ assertTrue(em.isLoaded(found, OWLClassA.getStrAttField().getName()));
+ }
+
+ @Test
+ public void isLoadedReturnsFalseForNonManagedInstance() throws Exception {
+ final OWLClassA a = Generators.generateOwlClassAInstance();
+ when(uow.contains(a)).thenReturn(false);
+ assertFalse(em.isLoaded(a, OWLClassA.getStrAttField().getName()));
+ }
+
+ @Test
+ public void isLoadedReturnsTrueForNonNullLazilyLoadedAttribute() throws Exception {
+ final OWLClassK inst = new OWLClassK();
+ inst.setUri(Generators.createIndividualIdentifier());
+ inst.setOwlClassE(new OWLClassE());
+ when(uow.contains(inst)).thenReturn(true);
+ doAnswer((invocationOnMock) -> inst).when(uow).readObject(eq(OWLClassK.class), eq(inst.getUri()), any(Descriptor.class));
+ final OWLClassK found = em.find(OWLClassK.class, inst.getUri());
+ assertTrue(em.isLoaded(found, OWLClassK.getOwlClassEField().getName()));
+ }
}