diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 6ad2b4db7..bf1662822 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -18,5 +18,6 @@ jobs:
with:
distribution: 'temurin'
java-version: 17
+ cache: maven
- name: Build with Maven
run: mvn -P javadoc -B package --file pom.xml
diff --git a/.gitignore b/.gitignore
index 056b3fefa..387d0f610 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,5 @@ build.out
**/catalog-v001.xml
.project
.classpath
-org.*
.factorypath
**/gen
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a5b0f03a1..9155f64df 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# JOPA - Change Log
+### 2.10.0 - 2026-05-29
+
+- Support optimizing entity loading from query result by using [fetch graphs](https://github.com/kbss-cvut/jopa/wiki/Performance#fetch-graph-based-entity-loading) (Enhancement #425, #431, #434).
+- Implement more predictable naming strategy in OWL2Java (Enhancement #432) - see the [README](jopa-owl2java/README.md) for details.
+- Ensure annotation-based entity descriptor is honored when loading entities from query result (Enhancement #437).
+- Dependency updates: RDF4J 5.3.1.
+
### 2.9.4 - 2026-04-17
- Change return type of `CriteriaBuilder.langMatches` to `Predicate` to allow including it in predicate lists.
diff --git a/datatype/pom.xml b/datatype/pom.xml
index a38f766a4..7471a4449 100644
--- a/datatype/pom.xml
+++ b/datatype/pom.xml
@@ -6,7 +6,7 @@
jopa-allcz.cvut.kbss.jopa
- 2.9.4
+ 2.10.0../pom.xml
diff --git a/jopa-api/pom.xml b/jopa-api/pom.xml
index 295e01174..de6e3cb80 100644
--- a/jopa-api/pom.xml
+++ b/jopa-api/pom.xml
@@ -6,7 +6,7 @@
cz.cvut.kbss.jopajopa-all
- 2.9.4
+ 2.10.0../pom.xml
diff --git a/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/AttributeNode.java b/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/AttributeNode.java
new file mode 100644
index 000000000..b96d4d48a
--- /dev/null
+++ b/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/AttributeNode.java
@@ -0,0 +1,25 @@
+package cz.cvut.kbss.jopa.model;
+
+import java.util.Map;
+
+/**
+ * Represents an attribute node of an entity graph.
+ *
+ * @param The type of the attribute
+ */
+public interface AttributeNode {
+
+ /**
+ * Gets the name of the attribute corresponding to the attribute node.
+ *
+ * @return name of the attribute
+ */
+ String getAttributeName();
+
+ /**
+ * Return the map of subgraphs associated with this attribute node.
+ *
+ * @return Map of subgraphs associated with this attribute node or empty Map if none have been defined
+ */
+ Map getSubgraphs();
+}
diff --git a/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/EntityGraph.java b/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/EntityGraph.java
new file mode 100644
index 000000000..c7da3a44d
--- /dev/null
+++ b/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/EntityGraph.java
@@ -0,0 +1,108 @@
+package cz.cvut.kbss.jopa.model;
+
+import cz.cvut.kbss.jopa.model.metamodel.Attribute;
+
+import java.util.List;
+
+/**
+ * This type represents the root of an entity graph that will be used as a template to define the attribute nodes and
+ * boundaries of a graph of entities and entity relationships.
+ *
+ * The root must be an entity type.
+ *
+ * The methods to add subgraphs implicitly create the corresponding attribute nodes as well; such attribute nodes should
+ * not be redundantly specified.
+ *
+ * @param The type of the root entity
+ */
+public interface EntityGraph {
+
+ /**
+ * Adds one or more attribute nodes to the entity graph.
+ *
+ * @param attribute Attribute to add
+ * @throws IllegalStateException If this {@code EntityGraph} has been statically defined
+ */
+ void addAttributeNodes(Attribute... attribute);
+
+ /**
+ * Adds one or more attribute nodes to the entity graph.
+ *
+ * @param attributeName Name of the attribute to add
+ * @throws IllegalStateException If this {@code EntityGraph} has been statically defined
+ * @throws IllegalArgumentException If the attribute is not an attribute of this managed type
+ */
+ void addAttributeNodes(String... attributeName);
+
+ /**
+ * Adds a node to the graph that corresponds to a managed type.
+ *
+ * This allows for construction of multi-node entity graphs that include related managed types.
+ *
+ * @param attribute Attribute to add
+ * @param Attribute type
+ * @return Subgraph for the attribute
+ * @throws IllegalStateException If this {@code EntityGraph} has been statically defined
+ * @throws IllegalArgumentException If the attribute's target type is not a managed type
+ */
+ Subgraph addSubgraph(Attribute attribute);
+
+ /**
+ * Adds a node to the graph that corresponds to a managed type.
+ *
+ * This allows for construction of multi-node entity graphs that include related managed types.
+ *
+ * @param attributeName Name of the attribute to add
+ * @param Attribute type
+ * @return Subgraph for the attribute
+ * @throws IllegalStateException If this {@code EntityGraph} has been statically defined
+ * @throws IllegalArgumentException If the attribute is not an attribute of this managed type
+ * @throws IllegalArgumentException If the attribute's target type is not a managed type
+ */
+ Subgraph addSubgraph(String attributeName);
+
+ /**
+ * Adds a node to the graph that corresponds to a managed type with inheritance.
+ *
+ * This allows for multiple subclass subgraphs to be defined for this node of the entity graph. Subclass subgraphs
+ * will automatically include the specified attributes of superclass subgraphs.
+ *
+ * @param attribute Attribute to add
+ * @param cls Entity subclass
+ * @param Attribute type
+ * @return Subgraph for the attribute
+ * @throws IllegalStateException If this {@code EntityGraph} has been statically defined
+ * @throws IllegalArgumentException If the attribute's target type is not a managed type
+ */
+ Subgraph extends X> addSubgraph(Attribute attribute, Class extends X> cls);
+
+ /**
+ * Add additional attributes to this entity graph that correspond to attributes of subclasses of this EntityGraph's
+ * entity type.
+ *
+ * Subclass subgraphs will automatically include the specified attributes of superclass subgraphs.
+ *
+ * @param cls Entity subclass
+ * @return Subgraph for the subclass
+ * @throws IllegalStateException If this {@code EntityGraph} has been statically defined
+ * @throws IllegalArgumentException If the type is not a managed type
+ */
+ Subgraph extends T> addSubclassSubgraph(Class extends T> cls);
+
+ /**
+ * Gets the attribute nodes corresponding to the attributes of this managed type that are included in the subgraph.
+ *
+ * @return List of attribute nodes included in the subgraph or empty list if none have been defined
+ */
+ List> getAttributeNodes();
+
+ /**
+ * Gets the name of a named EntityGraph (an entity graph defined by means of the NamedEntityGraph annotation, or
+ * added by means of the addNamedEntityGraph method).
+ *
+ * Returns null if the EntityGraph is not a named EntityGraph.
+ *
+ * @return Entity graph name, {@code null} if it is not named
+ */
+ String getName();
+}
diff --git a/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/EntityManager.java b/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/EntityManager.java
index 727efdd64..bcb39ae37 100644
--- a/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/EntityManager.java
+++ b/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/EntityManager.java
@@ -353,6 +353,26 @@ public interface EntityManager extends AutoCloseable {
*/
Query createNativeQuery(String sparqlString, String resultSetMapping);
+ /**
+ * Returns a mutable {@link EntityGraph} that can be used to dynamically create an entity graph.
+ *
+ * @param rootType Entity class
+ * @param Root entity class type
+ * @return Entity graph
+ */
+ EntityGraph createEntityGraph(Class rootType);
+
+ /**
+ * Obtain a named {@link EntityGraph}.
+ *
+ * The returned instance of EntityGraph should be considered immutable.
+ *
+ * @param graphName Name of an existing entity graph
+ * @return Named entity graph
+ * @throws IllegalArgumentException If there is no entity graph with the specified name
+ */
+ EntityGraph> getEntityGraph(String graphName);
+
/**
* Return an object of the specified type to allow access to the provider-specific API. If the provider's
* EntityManager implementation does not support the specified class, the {@link OWLPersistenceException} is
diff --git a/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/Subgraph.java b/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/Subgraph.java
new file mode 100644
index 000000000..49768d563
--- /dev/null
+++ b/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/Subgraph.java
@@ -0,0 +1,88 @@
+package cz.cvut.kbss.jopa.model;
+
+import cz.cvut.kbss.jopa.model.metamodel.Attribute;
+
+import java.util.List;
+
+/**
+ * This type represents a subgraph for an attribute node that corresponds to a Managed Type.
+ *
+ * Using this class, an entity subgraph can be embedded within an EntityGraph.
+ *
+ * @param The type of the attribute
+ */
+public interface Subgraph {
+
+ /**
+ * Adds one or more attribute nodes to the entity graph.
+ *
+ * @param attribute Attribute to add
+ * @throws IllegalStateException If this {@code EntityGraph} has been statically defined
+ */
+ void addAttributeNodes(Attribute... attribute);
+
+ /**
+ * Adds one or more attribute nodes to the entity graph.
+ *
+ * @param attributeName Name of the attribute to add
+ * @throws IllegalStateException If this {@code EntityGraph} has been statically defined
+ * @throws IllegalArgumentException If the attribute is not an attribute of this managed type
+ */
+ void addAttributeNodes(String... attributeName);
+
+ /**
+ * Adds a node to the graph that corresponds to a managed type.
+ *
+ * This allows for construction of multi-node entity graphs that include related managed types.
+ *
+ * @param attribute Attribute to add
+ * @param Attribute type
+ * @return Subgraph for the attribute
+ * @throws IllegalStateException If this {@code EntityGraph} has been statically defined
+ * @throws IllegalArgumentException If the attribute's target type is not a managed type
+ */
+ Subgraph addSubgraph(Attribute attribute);
+
+ /**
+ * Adds a node to the graph that corresponds to a managed type.
+ *
+ * This allows for construction of multi-node entity graphs that include related managed types.
+ *
+ * @param attributeName Name of the attribute to add
+ * @param Attribute type
+ * @return Subgraph for the attribute
+ * @throws IllegalStateException If this {@code EntityGraph} has been statically defined
+ * @throws IllegalArgumentException If the attribute is not an attribute of this managed type
+ * @throws IllegalArgumentException If the attribute's target type is not a managed type
+ */
+ Subgraph addSubgraph(String attributeName);
+
+ /**
+ * Adds a node to the graph that corresponds to a managed type with inheritance.
+ *
+ * This allows for multiple subclass subgraphs to be defined for this node of the entity graph. Subclass subgraphs
+ * will automatically include the specified attributes of superclass subgraphs.
+ *
+ * @param attribute Attribute to add
+ * @param cls Entity subclass
+ * @param Attribute type
+ * @return Subgraph for the attribute
+ * @throws IllegalStateException If this {@code EntityGraph} has been statically defined
+ * @throws IllegalArgumentException If the attribute's target type is not a managed type
+ */
+ Subgraph extends X> addSubgraph(Attribute attribute, Class extends X> cls);
+
+ /**
+ * Gets the attribute nodes corresponding to the attributes of this managed type that are included in the subgraph.
+ *
+ * @return List of attribute nodes included in the subgraph or empty list if none have been defined
+ */
+ List> getAttributeNodes();
+
+ /**
+ * Gets the type for which this subgraph was defined.
+ *
+ * @return managed type referenced by the subgraph
+ */
+ Class getClassType();
+}
diff --git a/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/annotations/NamedAttributeNode.java b/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/annotations/NamedAttributeNode.java
new file mode 100644
index 000000000..f82e88053
--- /dev/null
+++ b/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/annotations/NamedAttributeNode.java
@@ -0,0 +1,28 @@
+package cz.cvut.kbss.jopa.model.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A NamedAttributeNode is a member element of a {@link NamedEntityGraph}.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface NamedAttributeNode {
+
+ /**
+ * (Required) The name of the attribute that must be included in the graph.
+ */
+ String value();
+
+ /**
+ * (Optional) If the attribute references a managed type that has its own AttributeNodes, this element is used to
+ * refer to that {@link NamedSubgraph} definition.
+ *
+ * If the target type has inheritance, multiple subgraphs can be specified.These additional subgraphs are intended
+ * to add subclass-specific attributes. Superclass subgraph entries will be merged into subclass subgraphs.
+ *
+ * The value of this element is the name of the subgraph as specified by the name element of the corresponding
+ * NamedSubgraph element. If multiple subgraphs are specified due to inheritance, they are referenced by this name.
+ */
+ String subgraph() default "";
+}
diff --git a/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/annotations/NamedEntityGraph.java b/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/annotations/NamedEntityGraph.java
new file mode 100644
index 000000000..697b2b352
--- /dev/null
+++ b/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/annotations/NamedEntityGraph.java
@@ -0,0 +1,53 @@
+package cz.cvut.kbss.jopa.model.annotations;
+
+import cz.cvut.kbss.jopa.model.EntityManager;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Defines a named entity graph.
+ *
+ * This annotation must be applied to the root entity of the graph, and specifies the limits of the graph of associated
+ * attributes and entities fetched when an operation which retrieves an instance or instances of the root entity is
+ * executed.
+ *
+ * A reference to a named entity graph may be obtained by calling {@link EntityManager#getEntityGraph(String)}, and may
+ * be passed as hint to {@link cz.cvut.kbss.jopa.model.query.TypedQuery}.
+ */
+@Repeatable(NamedEntityGraphs.class)
+@Target(value = ElementType.TYPE)
+@Retention(value = RetentionPolicy.RUNTIME)
+public @interface NamedEntityGraph {
+
+ /**
+ * (Optional) The name used to identify the entity graph in calls to {@link EntityManager#getEntityGraph(String)}.
+ *
+ * If no name is explicitly specified, the name defaults to the entity name of the annotated root entity. Entity
+ * graph names must be unique within the persistence unit.
+ */
+ String name() default "";
+
+ /**
+ * (Optional) A list of attributes of the entity that are included in this graph.
+ */
+ NamedAttributeNode[] attributeNodes() default {};
+
+ /**
+ * (Optional) Includes all the attributes of the annotated entity class as attribute nodes in the
+ * {@code NamedEntityGraph} without the need to explicitly list them.
+ *
+ * Included attributes can still be fully specified by an attribute node referencing a subgraph.
+ */
+ boolean includeAllAttributes() default false;
+
+ /**
+ * (Optional) A list of subgraphs that are included in the entity graph.
+ *
+ * These are referenced by name from {@code NamedAttributeNode} definitions.
+ */
+ NamedSubgraph[] subgraphs() default {};
+}
diff --git a/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/annotations/NamedEntityGraphs.java b/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/annotations/NamedEntityGraphs.java
new file mode 100644
index 000000000..3448f4564
--- /dev/null
+++ b/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/annotations/NamedEntityGraphs.java
@@ -0,0 +1,16 @@
+package cz.cvut.kbss.jopa.model.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Used to group {@link NamedEntityGraph} annotations.
+ */
+@Target(value = ElementType.TYPE)
+@Retention(value = RetentionPolicy.RUNTIME)
+public @interface NamedEntityGraphs {
+
+ NamedEntityGraph[] value();
+}
diff --git a/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/annotations/NamedSubgraph.java b/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/annotations/NamedSubgraph.java
new file mode 100644
index 000000000..f8857523b
--- /dev/null
+++ b/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/annotations/NamedSubgraph.java
@@ -0,0 +1,31 @@
+package cz.cvut.kbss.jopa.model.annotations;
+
+/**
+ * A NamedSubgraph is a member element of a {@link NamedEntityGraph}.
+ *
+ * The NamedSubgraph is only referenced from within its containing {@code NamedEntityGraph} and cannot be referenced
+ * independently. It is referenced by its {@link #name()} from a {@link NamedAttributeNode} element of the
+ * {@code NamedEntityGraph}.
+ */
+public @interface NamedSubgraph {
+
+ /**
+ * (Required) The name of the subgraph as referenced from a NamedAttributeNode element.
+ */
+ String name();
+
+ /**
+ * (Required) The list of the attributes of the class that must be included.
+ *
+ * If the named subgraph corresponds to a subclass of the class referenced by the corresponding attribute node, then
+ * only subclass-specific attributes are listed.
+ */
+ NamedAttributeNode[] attributeNodes();
+
+ /**
+ * (Optional) The type represented by this subgraph.
+ *
+ * The element must be specified when this subgraph is extending a definition on behalf of a subclass.
+ */
+ Class> type() default void.class;
+}
diff --git a/jopa-distribution/pom.xml b/jopa-distribution/pom.xml
index 2009c9c66..a3a98f345 100644
--- a/jopa-distribution/pom.xml
+++ b/jopa-distribution/pom.xml
@@ -6,7 +6,7 @@
cz.cvut.kbss.jopajopa-all
- 2.9.4
+ 2.10.0../pom.xml
diff --git a/jopa-impl/pom.xml b/jopa-impl/pom.xml
index f74296bf7..c8a8b6fce 100644
--- a/jopa-impl/pom.xml
+++ b/jopa-impl/pom.xml
@@ -6,7 +6,7 @@
cz.cvut.kbss.jopajopa-all
- 2.9.4
+ 2.10.0../pom.xml
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/loaders/NamedEntityGraphLoader.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/loaders/NamedEntityGraphLoader.java
new file mode 100644
index 000000000..0805b940d
--- /dev/null
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/loaders/NamedEntityGraphLoader.java
@@ -0,0 +1,44 @@
+package cz.cvut.kbss.jopa.loaders;
+
+import cz.cvut.kbss.jopa.model.annotations.NamedEntityGraph;
+import cz.cvut.kbss.jopa.model.annotations.NamedEntityGraphs;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+
+/**
+ * Manages {@link cz.cvut.kbss.jopa.model.annotations.NamedEntityGraph} instances discovered during classpath
+ * processing.
+ *
+ * The discovered annotations are tracked together with the class on which they were declared, so that the root entity
+ * type can be later resolved when the entity graph is being built.
+ */
+public class NamedEntityGraphLoader implements Consumer> {
+
+ private final Map, Set> graphDeclarations = new HashMap<>();
+
+ @Override
+ public void accept(Class> aClass) {
+ final NamedEntityGraph graphDeclaration = aClass.getDeclaredAnnotation(NamedEntityGraph.class);
+ if (graphDeclaration != null) {
+ addDeclaration(aClass, graphDeclaration);
+ }
+ final NamedEntityGraphs graphDeclarationSet = aClass.getDeclaredAnnotation(NamedEntityGraphs.class);
+ if (graphDeclarationSet != null) {
+ for (NamedEntityGraph g : graphDeclarationSet.value()) {
+ addDeclaration(aClass, g);
+ }
+ }
+ }
+
+ private void addDeclaration(Class> aClass, NamedEntityGraph declaration) {
+ graphDeclarations.computeIfAbsent(aClass, k -> new HashSet<>()).add(declaration);
+ }
+
+ public Map, Set> getGraphDeclarations() {
+ return graphDeclarations;
+ }
+}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/loaders/PersistenceUnitClassFinder.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/loaders/PersistenceUnitClassFinder.java
index aa9c9f95b..049ca4349 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/loaders/PersistenceUnitClassFinder.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/loaders/PersistenceUnitClassFinder.java
@@ -19,6 +19,7 @@
import cz.cvut.kbss.jopa.exception.MetamodelInitializationException;
import cz.cvut.kbss.jopa.model.JOPAPersistenceProperties;
+import cz.cvut.kbss.jopa.model.annotations.NamedEntityGraph;
import cz.cvut.kbss.jopa.model.annotations.SparqlResultSetMapping;
import cz.cvut.kbss.jopa.oom.converter.ConverterWrapper;
import cz.cvut.kbss.jopa.utils.Configuration;
@@ -39,6 +40,7 @@ public class PersistenceUnitClassFinder {
private final EntityLoader entityLoader = new EntityLoader();
private final ResultSetMappingLoader resultSetMappingLoader = new ResultSetMappingLoader();
private final ConverterLoader converterLoader = new ConverterLoader();
+ private final NamedEntityGraphLoader namedEntityGraphLoader = new NamedEntityGraphLoader();
private boolean scanned = false;
@@ -65,6 +67,7 @@ public void scanClasspath(Configuration configuration) {
classpathScanner.addListener(entityLoader);
classpathScanner.addListener(resultSetMappingLoader);
classpathScanner.addListener(converterLoader);
+ classpathScanner.addListener(namedEntityGraphLoader);
Stream.of(toScan).map(String::trim).forEach(classpathScanner::processClasses);
this.scanned = true;
}
@@ -100,6 +103,19 @@ public Set getResultSetMappings() {
return resultSetMappingLoader.getMappings();
}
+ /**
+ * Gets {@link NamedEntityGraph}s found during classpath scanning.
+ *
+ * The annotations are mapped by the class on which they were declared, so that the root entity type can be later
+ * resolved when the entity graph is being built.
+ *
+ * @return Map of classes to named entity graph annotations declared on them
+ */
+ public Map, Set> getNamedEntityGraphs() {
+ assert scanned;
+ return namedEntityGraphLoader.getGraphDeclarations();
+ }
+
/**
* Gets {@link cz.cvut.kbss.jopa.model.AttributeConverter} implementations found during classpath scanning.
*
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/AbstractQuery.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/AbstractQuery.java
index fa356fc76..d08ecb15a 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/AbstractQuery.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/AbstractQuery.java
@@ -24,11 +24,9 @@
import cz.cvut.kbss.jopa.model.query.Query;
import cz.cvut.kbss.jopa.query.QueryHolder;
import cz.cvut.kbss.jopa.sessions.ConnectionWrapper;
-import cz.cvut.kbss.jopa.utils.ThrowingConsumer;
import cz.cvut.kbss.ontodriver.ResultSet;
import cz.cvut.kbss.ontodriver.Statement;
import cz.cvut.kbss.ontodriver.exception.OntoDriverException;
-import cz.cvut.kbss.ontodriver.iteration.ResultRow;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -121,7 +119,7 @@ public void executeUpdate() {
}
}
- private Statement initQueryStatement() {
+ Statement initQueryStatement() {
final Statement stmt = connection.createStatement();
applyQueryHints(stmt);
logQuery();
@@ -265,21 +263,6 @@ void checkNumericParameter(int param, String name) {
}
}
- /**
- * Executes the query and lets the specified consumer deal with each row in the result set.
- *
- * @param consumer Called for every row in the result set
- * @throws OntoDriverException When something goes wrong during query evaluation or result set processing
- */
- void executeQuery(ThrowingConsumer consumer) throws OntoDriverException {
- try (final Statement stmt = initQueryStatement()) {
- final ResultSet rs = stmt.executeQuery(query.assembleQuery());
- for (ResultRow row : rs) {
- consumer.accept(row);
- }
- }
- }
-
private void applyQueryHints(Statement statement) {
hints.forEach((hint, value) -> QueryHintsHandler.apply(hint, value, this, statement));
}
@@ -287,6 +270,7 @@ private void applyQueryHints(Statement statement) {
Stream executeQueryForStream(QueryResultLoader resultLoader) throws OntoDriverException {
final Statement stmt = initQueryStatement();
final ResultSet rs = stmt.executeQuery(query.assembleQuery());
+ resultLoader.init(rs);
final Runnable closeHandler = () -> {
try {
stmt.close();
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/AttributeNodeImpl.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/AttributeNodeImpl.java
new file mode 100644
index 000000000..425e98a76
--- /dev/null
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/AttributeNodeImpl.java
@@ -0,0 +1,40 @@
+package cz.cvut.kbss.jopa.model;
+
+import cz.cvut.kbss.jopa.model.metamodel.Attribute;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class AttributeNodeImpl implements AttributeNode {
+
+ private final Attribute, X> attribute;
+
+ private final Map subgraphs = new HashMap<>();
+
+ public AttributeNodeImpl(Attribute, X> attribute) {
+ this.attribute = attribute;
+ }
+
+ public Attribute, X> getAttribute() {
+ return attribute;
+ }
+
+ @Override
+ public String getAttributeName() {
+ return attribute.getName();
+ }
+
+ @Override
+ public Map getSubgraphs() {
+ return subgraphs;
+ }
+
+ void addSubgraph(EntityGraphImpl> graph) {
+ subgraphs.put(graph.getClassType(), graph);
+ }
+
+ @Override
+ public String toString() {
+ return attribute.toString();
+ }
+}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/EntityGraphImpl.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/EntityGraphImpl.java
new file mode 100644
index 000000000..04d8b0b02
--- /dev/null
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/EntityGraphImpl.java
@@ -0,0 +1,99 @@
+package cz.cvut.kbss.jopa.model;
+
+import cz.cvut.kbss.jopa.model.metamodel.Attribute;
+import cz.cvut.kbss.jopa.model.metamodel.ManagedType;
+import cz.cvut.kbss.jopa.model.metamodel.MetamodelClassMapper;
+import cz.cvut.kbss.jopa.model.metamodel.PluralAttribute;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class EntityGraphImpl implements EntityGraph, Subgraph {
+
+ private final String name;
+
+ private final ManagedType entityType;
+ private final MetamodelClassMapper metamodel;
+
+ private final Map> attributeNodes = new HashMap<>();
+
+ public EntityGraphImpl(ManagedType entityType, MetamodelClassMapper metamodel) {
+ this.entityType = entityType;
+ this.metamodel = metamodel;
+ this.name = null;
+ }
+
+ public EntityGraphImpl(String name, ManagedType entityType, MetamodelClassMapper metamodel) {
+ this.name = name;
+ this.entityType = entityType;
+ this.metamodel = metamodel;
+ }
+
+ @Override
+ public void addAttributeNodes(Attribute... attribute) {
+ for (Attribute att : attribute) {
+ attributeNodes.put(att.getName(), new AttributeNodeImpl<>(att));
+ }
+ }
+
+ @Override
+ public void addAttributeNodes(String... attributeName) {
+ for (String attName : attributeName) {
+ final Attribute super T, ?> att = entityType.getAttribute(attName);
+ assert att != null;
+ attributeNodes.put(attName, new AttributeNodeImpl<>(att));
+ }
+ }
+
+ @Override
+ public EntityGraphImpl addSubgraph(Attribute attribute) {
+ final AttributeNodeImpl node = (AttributeNodeImpl) attributeNodes.computeIfAbsent(attribute.getName(), k -> new AttributeNodeImpl<>(attribute));
+ Class> javaType = attribute.getJavaType();
+ if (attribute.isCollection()) {
+ javaType = ((PluralAttribute) attribute).getBindableJavaType();
+ }
+ final EntityGraphImpl g = new EntityGraphImpl(metamodel.entity(javaType), metamodel);
+ node.addSubgraph(g);
+ return g;
+ }
+
+ @Override
+ public Subgraph addSubgraph(String attributeName) {
+ final Attribute att = (Attribute) entityType.getAttribute(attributeName);
+ return addSubgraph(att);
+ }
+
+ @Override
+ public Subgraph extends X> addSubgraph(Attribute attribute, Class extends X> cls) {
+ final EntityGraphImpl g = addSubgraph(attribute);
+ g.addSubclassSubgraph(cls);
+ return g;
+ }
+
+ @Override
+ public Subgraph extends T> addSubclassSubgraph(Class extends T> cls) {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public List> getAttributeNodes() {
+ return new ArrayList<>(attributeNodes.values());
+ }
+
+ AttributeNodeImpl> getAttributeNode(String name) {
+ return attributeNodes.get(name);
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public Class getClassType() {
+ return entityType.getJavaType();
+ }
+}
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 f75a83ece..8815a9fc1 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
@@ -469,9 +469,13 @@ public boolean isLoaded(Object object) {
public QueryImpl createQuery(String qlString) {
ensureOpen();
final QueryImpl q = getCurrentPersistenceContext().sparqlQueryFactory().createQuery(qlString);
+ initQueryCallbacks(q);
+ return q;
+ }
+
+ private void initQueryCallbacks(AbstractQuery q) {
q.setRollbackOnlyMarker(this::markTransactionForRollback);
q.setEnsureOpenProcedure(this::ensureOpen);
- return q;
}
@Override
@@ -483,19 +487,25 @@ public TypedQuery createQuery(CriteriaQuery criteriaQuery) {
LOG.debug("CriteriaQuery translate to SOQL query: {}", soqlQuery);
final TypedQueryImpl q = getCurrentPersistenceContext().sparqlQueryFactory()
.createQuery(soqlQuery, query.getResultType());
- q.setRollbackOnlyMarker(this::markTransactionForRollback);
- q.setEnsureOpenProcedure(this::ensureOpen);
+ initQueryCallbacks(q);
+ initTypedQueryDescriptor(query.getResultType(), q);
parameterFiller.setValuesToRegisteredParameters(q);
return q;
}
+ private void initTypedQueryDescriptor(Class resultClass, TypedQuery q) {
+ if (getCurrentPersistenceContext().isEntityType(resultClass)) {
+ q.setDescriptor(descriptorFactory.createDescriptor(resultClass));
+ }
+ }
+
@Override
public TypedQueryImpl createQuery(String query, Class resultClass) {
ensureOpen();
final TypedQueryImpl q = getCurrentPersistenceContext().sparqlQueryFactory().createQuery(query, resultClass);
- q.setRollbackOnlyMarker(this::markTransactionForRollback);
- q.setEnsureOpenProcedure(this::ensureOpen);
+ initQueryCallbacks(q);
+ initTypedQueryDescriptor(resultClass, q);
return q;
}
@@ -503,8 +513,7 @@ public TypedQueryImpl createQuery(String query, Class resultClass) {
public QueryImpl createNativeQuery(String sparqlString) {
ensureOpen();
final QueryImpl q = getCurrentPersistenceContext().sparqlQueryFactory().createNativeQuery(sparqlString);
- q.setRollbackOnlyMarker(this::markTransactionForRollback);
- q.setEnsureOpenProcedure(this::ensureOpen);
+ initQueryCallbacks(q);
return q;
}
@@ -513,8 +522,8 @@ public TypedQueryImpl createNativeQuery(String sparqlString, Class res
ensureOpen();
final TypedQueryImpl q = getCurrentPersistenceContext().sparqlQueryFactory()
.createNativeQuery(sparqlString, resultClass);
- q.setRollbackOnlyMarker(this::markTransactionForRollback);
- q.setEnsureOpenProcedure(this::ensureOpen);
+ initQueryCallbacks(q);
+ initTypedQueryDescriptor(resultClass, q);
return q;
}
@@ -523,8 +532,7 @@ public QueryImpl createNativeQuery(String sparqlString, String resultSetMapping)
ensureOpen();
final QueryImpl q = getCurrentPersistenceContext().sparqlQueryFactory()
.createNativeQuery(sparqlString, resultSetMapping);
- q.setRollbackOnlyMarker(this::markTransactionForRollback);
- q.setEnsureOpenProcedure(this::ensureOpen);
+ initQueryCallbacks(q);
return q;
}
@@ -532,8 +540,7 @@ public QueryImpl createNativeQuery(String sparqlString, String resultSetMapping)
public QueryImpl createNamedQuery(String name) {
ensureOpen();
final QueryImpl q = getCurrentPersistenceContext().sparqlQueryFactory().createNamedQuery(name);
- q.setRollbackOnlyMarker(this::markTransactionForRollback);
- q.setEnsureOpenProcedure(this::ensureOpen);
+ initQueryCallbacks(q);
return q;
}
@@ -542,8 +549,8 @@ public TypedQueryImpl createNamedQuery(String name, Class resultClass)
ensureOpen();
final TypedQueryImpl q = getCurrentPersistenceContext().sparqlQueryFactory()
.createNamedQuery(name, resultClass);
- q.setRollbackOnlyMarker(this::markTransactionForRollback);
- q.setEnsureOpenProcedure(this::ensureOpen);
+ initQueryCallbacks(q);
+ initTypedQueryDescriptor(resultClass, q);
return q;
}
@@ -570,6 +577,18 @@ public CriteriaBuilder getCriteriaBuilder() {
return getCurrentPersistenceContext().getCriteriaBuilder();
}
+ @Override
+ public EntityGraph createEntityGraph(Class rootType) {
+ ensureOpen();
+ return getCurrentPersistenceContext().createEntityGraph(rootType);
+ }
+
+ @Override
+ public EntityGraph> getEntityGraph(String graphName) {
+ ensureOpen();
+ return getCurrentPersistenceContext().getEntityGraph(graphName);
+ }
+
@Override
public T unwrap(Class cls) {
ensureOpen();
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/MetamodelImpl.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/MetamodelImpl.java
index 62f30cf72..b39986af0 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/MetamodelImpl.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/MetamodelImpl.java
@@ -24,6 +24,7 @@
import cz.cvut.kbss.jopa.model.metamodel.ManagedType;
import cz.cvut.kbss.jopa.model.metamodel.Metamodel;
import cz.cvut.kbss.jopa.model.metamodel.MetamodelBuilder;
+import cz.cvut.kbss.jopa.model.metamodel.MetamodelClassMapper;
import cz.cvut.kbss.jopa.model.metamodel.StaticMetamodelInitializer;
import cz.cvut.kbss.jopa.proxy.lazy.gen.LazyLoadingEntityProxyGenerator;
import cz.cvut.kbss.jopa.proxy.reference.EntityReferenceProxyGenerator;
@@ -47,7 +48,7 @@
import java.util.regex.Pattern;
import java.util.stream.Collectors;
-public class MetamodelImpl implements Metamodel, MetamodelProvider {
+public class MetamodelImpl implements Metamodel, MetamodelProvider, MetamodelClassMapper {
private static final Logger LOG = LoggerFactory.getLogger(MetamodelImpl.class);
@@ -62,6 +63,7 @@ public class MetamodelImpl implements Metamodel, MetamodelProvider {
private NamedQueryManager namedQueryManager;
private ResultSetMappingManager resultSetMappingManager;
+ private NamedEntityGraphManager namedEntityGraphManager;
private NamespaceResolver namespaceResolver;
@@ -95,6 +97,7 @@ public void build(PersistenceUnitClassFinder classFinder) {
initFromMetamodelBuilder(metamodelBuilder);
this.namedQueryManager = metamodelBuilder.getNamedQueryManager();
this.resultSetMappingManager = metamodelBuilder.getResultSetMappingManager();
+ this.namedEntityGraphManager = metamodelBuilder.getNamedEntityGraphManager();
new StaticMetamodelInitializer(this).initializeStaticMetamodel();
}
@@ -169,6 +172,10 @@ public ResultSetMappingManager getResultSetMappingManager() {
return resultSetMappingManager;
}
+ public NamedEntityGraphManager getNamedEntityGraphManager() {
+ return namedEntityGraphManager;
+ }
+
@Override
public Set getModuleExtractionExtraSignature() {
return Collections.unmodifiableSet(getSignatureInternal());
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/NamedEntityGraphManager.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/NamedEntityGraphManager.java
new file mode 100644
index 000000000..089a94fc2
--- /dev/null
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/NamedEntityGraphManager.java
@@ -0,0 +1,56 @@
+package cz.cvut.kbss.jopa.model;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Manages named {@link EntityGraph}s, which are used to define fetching strategies for entities.
+ *
+ * This class is not synchronized because it is expected that modifications are done serially when building the
+ * persistence unit and all further interactions are read-only.
+ */
+public class NamedEntityGraphManager {
+
+ private final Map> namedEntityGraphs = new HashMap<>();
+
+ /**
+ * Registers the specified {@link EntityGraph} under the specified name.
+ *
+ * @param graphName Name of the named entity graph
+ * @param entityGraph Entity graph to register
+ * @throws IllegalArgumentException If a named entity graph with the same name already exists
+ */
+ public void addEntityGraph(String graphName, EntityGraph> entityGraph) {
+ Objects.requireNonNull(graphName);
+ Objects.requireNonNull(entityGraph);
+ if (namedEntityGraphs.containsKey(graphName)) {
+ throw new IllegalArgumentException("Entity graph " + graphName + " already exists in this persistence unit.");
+ }
+ namedEntityGraphs.put(graphName, entityGraph);
+ }
+
+ /**
+ * Gets an {@link EntityGraph} with the specified name.
+ *
+ * @param graphName Name of entity graph to retrieve
+ * @return Matching entity graph
+ * @throws IllegalArgumentException If no such entity graph is found
+ */
+ public EntityGraph> getEntityGraph(String graphName) {
+ if (!namedEntityGraphs.containsKey(graphName)) {
+ throw new IllegalArgumentException("Entity graph " + graphName + " not found in this persistence unit.");
+ }
+ return namedEntityGraphs.get(graphName);
+ }
+
+ /**
+ * Checks whether this manager contains a graph with the specified name
+ *
+ * @param graphName Name to search for
+ * @return {@code true} if such graph is registered here, {@code false} otherwise
+ */
+ public boolean hasEntityGraph(String graphName) {
+ return namedEntityGraphs.containsKey(graphName);
+ }
+}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/NamedEntityGraphProcessor.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/NamedEntityGraphProcessor.java
new file mode 100644
index 000000000..6107b777a
--- /dev/null
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/NamedEntityGraphProcessor.java
@@ -0,0 +1,117 @@
+package cz.cvut.kbss.jopa.model;
+
+import cz.cvut.kbss.jopa.model.annotations.NamedAttributeNode;
+import cz.cvut.kbss.jopa.model.annotations.NamedEntityGraph;
+import cz.cvut.kbss.jopa.model.annotations.NamedSubgraph;
+import cz.cvut.kbss.jopa.model.metamodel.AbstractIdentifiableType;
+import cz.cvut.kbss.jopa.model.metamodel.Attribute;
+import cz.cvut.kbss.jopa.model.metamodel.MetamodelClassMapper;
+import cz.cvut.kbss.jopa.model.metamodel.PluralAttribute;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Builds {@link EntityGraphImpl} instances from {@link NamedEntityGraph} annotations discovered on entity classes.
+ */
+public class NamedEntityGraphProcessor {
+
+ private final NamedEntityGraphManager manager;
+
+ private final MetamodelClassMapper metamodel;
+
+ public NamedEntityGraphProcessor(MetamodelClassMapper metamodel) {
+ this.metamodel = metamodel;
+ this.manager = new NamedEntityGraphManager();
+ }
+
+ public NamedEntityGraphManager getManager() {
+ return manager;
+ }
+
+ /**
+ * Builds an entity graph from the specified {@link NamedEntityGraph} declaration and registers it in the manager.
+ *
+ * @param rootType The class on which the {@code NamedEntityGraph} annotation was declared
+ * @param declaration Named entity graph annotation
+ */
+ public void buildEntityGraph(Class> rootType, NamedEntityGraph declaration) {
+ Objects.requireNonNull(rootType);
+ Objects.requireNonNull(declaration);
+ final String name = graphName(rootType, declaration);
+ manager.addEntityGraph(name, createGraph(rootType, name, declaration));
+ }
+
+ private static String graphName(Class> rootType, NamedEntityGraph declaration) {
+ if (declaration.name().isEmpty()) {
+ return rootType.getSimpleName();
+ }
+ return declaration.name();
+ }
+
+ private EntityGraphImpl createGraph(Class> rootType, String name, NamedEntityGraph declaration) {
+ final AbstractIdentifiableType entityType = metamodel.entity((Class) rootType);
+ final EntityGraphImpl graph = new EntityGraphImpl<>(name, entityType, metamodel);
+ final Map namedSubgraphs = indexSubgraphs(declaration.subgraphs());
+ addAttributeNodes(declaration.attributeNodes(), graph, entityType, namedSubgraphs);
+ return graph;
+ }
+
+ private void addAttributeNodes(NamedAttributeNode[] nodes, EntityGraphImpl graph,
+ AbstractIdentifiableType entityType,
+ Map namedSubgraphs) {
+ for (NamedAttributeNode node : nodes) {
+ graph.addAttributeNodes(node.value());
+ if (!node.subgraph().isEmpty()) {
+ attachSubgraph(graph, entityType, node, namedSubgraphs);
+ }
+ }
+ }
+
+ private void attachSubgraph(EntityGraphImpl> parent, AbstractIdentifiableType> rootEntityType,
+ NamedAttributeNode node, Map namedSubgraphs) {
+ final NamedSubgraph subDecl = namedSubgraphs.get(node.subgraph());
+ if (subDecl == null) {
+ throw new IllegalArgumentException(
+ "Named subgraph '" + node.subgraph() + "' referenced by attribute '" + node.value() +
+ "' is not declared in the entity graph.");
+ }
+ final Class> subType = resolveSubgraphType(rootEntityType, node.value(), subDecl);
+ final EntityGraphImpl> sub = buildSubgraph(subType, subDecl, namedSubgraphs);
+ final AttributeNodeImpl> attrNode = parent.getAttributeNode(node.value());
+ attrNode.addSubgraph(sub);
+ }
+
+ private EntityGraphImpl buildSubgraph(Class> subType, NamedSubgraph declaration,
+ Map namedSubgraphs) {
+ final AbstractIdentifiableType entityType = metamodel.entity((Class) subType);
+ final EntityGraphImpl graph = new EntityGraphImpl<>(declaration.name(), entityType, metamodel);
+ addAttributeNodes(declaration.attributeNodes(), graph, entityType, namedSubgraphs);
+ return graph;
+ }
+
+ private static Class> resolveSubgraphType(AbstractIdentifiableType> rootEntityType, String attributeName,
+ NamedSubgraph subDecl) {
+ if (subDecl.type() != void.class) {
+ return subDecl.type();
+ }
+ final Attribute, ?> att = rootEntityType.getAttribute(attributeName);
+ assert att != null;
+ if (att.isCollection()) {
+ return ((PluralAttribute, ?, ?>) att).getBindableJavaType();
+ }
+ return att.getJavaType();
+ }
+
+ private static Map indexSubgraphs(NamedSubgraph[] subgraphs) {
+ if (subgraphs.length == 0) {
+ return Map.of();
+ }
+ final Map result = new HashMap<>(subgraphs.length);
+ for (NamedSubgraph s : subgraphs) {
+ result.put(s.name(), s);
+ }
+ return result;
+ }
+}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/QueryHintsHandler.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/QueryHintsHandler.java
index 2e2053793..00db8dc74 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/QueryHintsHandler.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/QueryHintsHandler.java
@@ -97,6 +97,7 @@ abstract static class Hint {
registerHint(new DisableInferenceHint());
registerHint(new TargetOntologyHint());
registerHint(new OptimizedQueryResultEntityLoadingHint());
+ registerHint(new FetchGraphHint());
}
Hint(String name, Object defaultValue) {
@@ -225,4 +226,15 @@ void applyToQuery(Object hintValue, AbstractQuery query, Statement statement) {
// Do nothing, the optimizer is enabled as soon as the hint is set
}
}
+
+ protected static class FetchGraphHint extends Hint {
+ public FetchGraphHint() {
+ super(QueryHints.FETCH_GRAPH, null);
+ }
+
+ @Override
+ void applyToQuery(Object hintValue, AbstractQuery query, Statement statement) {
+ // Do nothing, the query pulls the fetch graph from the hint automatically
+ }
+ }
}
diff --git a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/QueryImpl.java b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/QueryImpl.java
index bead95ffc..c11ac3055 100644
--- a/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/QueryImpl.java
+++ b/jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/QueryImpl.java
@@ -23,6 +23,8 @@
import cz.cvut.kbss.jopa.model.query.Query;
import cz.cvut.kbss.jopa.query.QueryHolder;
import cz.cvut.kbss.jopa.sessions.ConnectionWrapper;
+import cz.cvut.kbss.ontodriver.ResultSet;
+import cz.cvut.kbss.ontodriver.Statement;
import cz.cvut.kbss.ontodriver.exception.OntoDriverException;
import cz.cvut.kbss.ontodriver.iteration.ResultRow;
@@ -57,7 +59,12 @@ public List getResultList() {
private List> getResultListImpl() throws OntoDriverException {
final List