You are viewing a plain text version of this content. The canonical link for it is here.
Posted to jdo-commits@db.apache.org by mb...@apache.org on 2020/01/03 19:45:28 UTC
[db-jdo] 01/01: JDO-778: JDOQLTypedQuery support for correlated
subqueries
This is an automated email from the ASF dual-hosted git repository.
mbo pushed a commit to branch JDO-778-2
in repository https://gitbox.apache.org/repos/asf/db-jdo.git
commit a3d385c548bf5aa63c9f0d9c3c8ca075c4fd85b7
Author: Michael Bouschen <mb...@apache.org>
AuthorDate: Fri Jan 3 20:44:53 2020 +0100
JDO-778: JDOQLTypedQuery support for correlated subqueries
- Removed the two subquery methods taking a ListExpression and taking a MapExpression as argument.
- Added TCK test case testing a correlated subquery using a list relationship.
- Added more test data for subquery test cases.
- Corresponding spec changes
---
api/src/main/java/javax/jdo/JDOQLTypedQuery.java | 25 ---
specification/OOO/Ch14-Query.odt | Bin 78557 -> 79033 bytes
.../jdoql/subqueries/CorrelatedSubqueries.java | 212 +++++++++++++++++----
tck/src/main/resources/conf/jdo-signatures.txt | 2 -
.../tck/pc/company/companyForSubqueriesTests.xml | 24 +++
5 files changed, 202 insertions(+), 61 deletions(-)
diff --git a/api/src/main/java/javax/jdo/JDOQLTypedQuery.java b/api/src/main/java/javax/jdo/JDOQLTypedQuery.java
index 19381b9..d91f85e 100644
--- a/api/src/main/java/javax/jdo/JDOQLTypedQuery.java
+++ b/api/src/main/java/javax/jdo/JDOQLTypedQuery.java
@@ -360,31 +360,6 @@ public interface JDOQLTypedQuery<T> extends Serializable, Closeable {
<E> JDOQLTypedSubquery<E> subquery(CollectionExpression<Collection<E>, E> candidateCollection, Class<E> candidate, String candidateAlias);
/**
- * Method to return a correlated subquery for use in this query.
- * The candidate collection of the subquery is defined using a list relationship of the outer query.
- * To obtain the expression for the subquery to link it back to this query, call "result(...)" on the subquery.
- * @param candidateList Expression defining the candidate list for the subquery
- * @param candidate Candidate for the subquery
- * @param candidateAlias Alias for the candidate
- * @return The subquery
- * @param <E> Candidate type for subquery
- */
- <E> JDOQLTypedSubquery<E> subquery(ListExpression<List<E>, E> candidateList, Class<E> candidate, String candidateAlias);
-
- /**
- * Method to return a correlated subquery for use in this query.
- * The candidate collection of the subquery is defined using a map relationship of the outer query.
- * To obtain the expression for the subquery to link it back to this query, call "result(...)" on the subquery.
- * @param candidateMap Expression defining the candidate map for the subquery
- * @param candidate Candidate for the subquery
- * @param candidateAlias Alias for the candidate
- * @return The subquery
- * @param <K> The key type of the map relationship
- * @param <V> The value type the map relationship
- */
- <K, V> JDOQLTypedSubquery<Map.Entry<K, V>> subquery(MapExpression<Map<K, V>, K, V> candidateMap, Class<Map.Entry<K, V>> candidate, String candidateAlias);
-
- /**
* Method to set the named parameters on this query prior to execution.
* All parameter values specified in this method will only be retained until the subsequent query execution.
* @param namedParamMap The map of parameter values keyed by their names.
diff --git a/specification/OOO/Ch14-Query.odt b/specification/OOO/Ch14-Query.odt
index 0d065a1..4c35a64 100644
Binary files a/specification/OOO/Ch14-Query.odt and b/specification/OOO/Ch14-Query.odt differ
diff --git a/tck/src/main/java/org/apache/jdo/tck/query/jdoql/subqueries/CorrelatedSubqueries.java b/tck/src/main/java/org/apache/jdo/tck/query/jdoql/subqueries/CorrelatedSubqueries.java
index ee535fd..b33adc4 100644
--- a/tck/src/main/java/org/apache/jdo/tck/query/jdoql/subqueries/CorrelatedSubqueries.java
+++ b/tck/src/main/java/org/apache/jdo/tck/query/jdoql/subqueries/CorrelatedSubqueries.java
@@ -19,12 +19,19 @@ package org.apache.jdo.tck.query.jdoql.subqueries;
import java.util.List;
-import javax.jdo.PersistenceManager;
+import javax.jdo.JDOQLTypedQuery;
+import javax.jdo.JDOQLTypedSubquery;
import javax.jdo.Query;
+import javax.jdo.Transaction;
+import javax.jdo.query.NumericExpression;
import org.apache.jdo.tck.JDO_Test;
import org.apache.jdo.tck.pc.company.CompanyModelReader;
import org.apache.jdo.tck.pc.company.Employee;
+import org.apache.jdo.tck.pc.company.FullTimeEmployee;
+import org.apache.jdo.tck.pc.company.MeetingRoom;
+import org.apache.jdo.tck.pc.company.QEmployee;
+import org.apache.jdo.tck.pc.company.QMeetingRoom;
import org.apache.jdo.tck.util.BatchTestRunner;
/**
@@ -44,7 +51,17 @@ public class CorrelatedSubqueries extends SubqueriesTest {
/** */
private static final String ASSERTION_FAILED =
"Assertion A14.6.2-56 (CorrelatedSubqueries) failed: ";
-
+
+ // Select employees who work more than the average of their department employees.
+ private static final String SINGLE_STRING_QUERY_COLLECTION_SUBQUERY =
+ "SELECT FROM " + Employee.class.getName() +
+ " WHERE this.weeklyhours > (SELECT AVG(e.weeklyhours) FROM this.department.employees e)";
+
+ // Select employees who work in a department having a meeting room with the maximum roomid.
+ private static final String SINGLE_STRING_QUERY_LIST_SUBQUERY =
+ "SELECT FROM " + Employee.class.getName() +
+ " WHERE (SELECT max(r.roomid) FROM this.department.meetingRooms r) == 3";
+
/**
* The <code>main</code> is called when the class
* is directly executed from the command line.
@@ -55,38 +72,165 @@ public class CorrelatedSubqueries extends SubqueriesTest {
}
/** */
- public void testPositive() throws Exception {
- PersistenceManager pm = getPM();
-
- List expectedResult = getTransientCompanyModelInstancesAsList(
- new String[]{"emp1", "emp2", "emp4", "emp6", "emp7", "emp10"});
-
- // Select employees who work more than the average of
- // their department employees.
- String singleStringJDOQL =
- "SELECT FROM " + Employee.class.getName() + " WHERE this.weeklyhours > " +
- "(SELECT AVG(e.weeklyhours) FROM this.department.employees e)";
-
- // API query
- Query sub = pm.newQuery(Employee.class);
- sub.setResult("avg(this.weeklyhours)");
- Query apiQuery = pm.newQuery(Employee.class);
- apiQuery.setFilter("this.weeklyhours> averageWeeklyhours");
- apiQuery.addSubquery(sub, "double averageWeeklyhours",
- "this.department.employees");
- executeJDOQuery(ASSERTION_FAILED, apiQuery, singleStringJDOQL,
- false, null, expectedResult, true);
-
- // API query against memory model
- List allEmployees = getAllEmployees(pm);
- apiQuery.setCandidates(allEmployees);
- executeJDOQuery(ASSERTION_FAILED, apiQuery, singleStringJDOQL,
- false, null, expectedResult, true);
-
- // single String JDOQL
- Query singleStringQuery = pm.newQuery(singleStringJDOQL);
- executeJDOQuery(ASSERTION_FAILED, singleStringQuery, singleStringJDOQL,
- false, null, expectedResult, true);
+ public void testCollectionApiQuery() {
+ Transaction tx = pm.currentTransaction();
+ try {
+ tx.begin();
+ List expected = getTransientCompanyModelInstancesAsList(
+ new String[]{"emp1", "emp2", "emp4", "emp6", "emp7", "emp10"});
+
+ // API query
+ try (Query apiQuery = pm.newQuery(Employee.class)) {
+ Query sub = pm.newQuery(Employee.class);
+ sub.setResult("avg(this.weeklyhours)");
+ apiQuery.setFilter("this.weeklyhours> averageWeeklyhours");
+ apiQuery.addSubquery(sub, "double averageWeeklyhours",
+ "this.department.employees");
+ List<FullTimeEmployee> emps = apiQuery.executeList();
+ checkQueryResultWithoutOrder(ASSERTION_FAILED, SINGLE_STRING_QUERY_COLLECTION_SUBQUERY, emps, expected);
+
+ // API query against memory model
+ List allEmployees = (List)pm.newQuery(Employee.class).execute();
+ apiQuery.setCandidates(allEmployees);
+ emps = apiQuery.executeList();
+ checkQueryResultWithoutOrder(ASSERTION_FAILED, SINGLE_STRING_QUERY_COLLECTION_SUBQUERY, emps, expected);
+ } catch (Exception ex) {
+ fail(ASSERTION_FAILED, ex.getLocalizedMessage());
+ }
+ tx.commit();
+ } finally {
+ if (tx.isActive()) {
+ tx.rollback();
+ }
+ }
+ }
+
+ /** */
+ public void testCollectionSingleStringQuery() {
+ Transaction tx = pm.currentTransaction();
+ try {
+ tx.begin();
+ List expected = getTransientCompanyModelInstancesAsList(
+ new String[]{"emp1", "emp2", "emp4", "emp6", "emp7", "emp10"});
+ // single String JDOQL
+ try (Query singleStringQuery = pm.newQuery(SINGLE_STRING_QUERY_COLLECTION_SUBQUERY)) {
+ List<FullTimeEmployee> emps = singleStringQuery.executeList();
+ checkQueryResultWithoutOrder(ASSERTION_FAILED, SINGLE_STRING_QUERY_COLLECTION_SUBQUERY, emps, expected);
+ } catch (Exception ex) {
+ fail(ASSERTION_FAILED, ex.getLocalizedMessage());
+ }
+ tx.commit();
+ } finally {
+ if (tx.isActive()) {
+ tx.rollback();
+ }
+ }
+ }
+
+ /** */
+ public void testCollectionJDOQLTypedQuery() {
+ Transaction tx = pm.currentTransaction();
+ try {
+ tx.begin();
+ List expected = getTransientCompanyModelInstancesAsList(
+ new String[]{"emp1", "emp2", "emp4", "emp6", "emp7", "emp10"});
+ try (JDOQLTypedQuery<Employee> q = pm.newJDOQLTypedQuery(Employee.class)) {
+ QEmployee cand = QEmployee.candidate();
+ JDOQLTypedSubquery<Employee> subquery = q.subquery(cand.department.employees, Employee.class,"e");
+ QEmployee candsub = QEmployee.candidate("e");
+ q.filter(cand.weeklyhours.gt(subquery.selectUnique(candsub.weeklyhours.avg())));
+ List<Employee> emps = q.executeList();
+ checkQueryResultWithoutOrder(ASSERTION_FAILED, SINGLE_STRING_QUERY_COLLECTION_SUBQUERY, emps, expected);
+ } catch (Exception ex) {
+ fail(ASSERTION_FAILED, ex.getLocalizedMessage());
+ }
+ tx.commit();
+ } finally {
+ if (tx.isActive()) {
+ tx.rollback();
+ }
+ }
+ }
+
+ /** */
+ public void testListApiQuery() {
+ Transaction tx = pm.currentTransaction();
+ try {
+ tx.begin();
+ List expected = getTransientCompanyModelInstancesAsList(
+ new String[]{"emp7", "emp8", "emp9", "emp10"});
+
+ // API query
+ try (Query apiQuery = pm.newQuery(Employee.class)) {
+ Query sub = pm.newQuery(MeetingRoom.class);
+ sub.setResult("max(roomid)");
+ apiQuery.setFilter("maxNumber == 3");
+ apiQuery.addSubquery(sub, "long maxNumber", "this.department.meetingRooms");
+ List<FullTimeEmployee> emps = apiQuery.executeList();
+ checkQueryResultWithoutOrder(ASSERTION_FAILED, SINGLE_STRING_QUERY_LIST_SUBQUERY, emps, expected);
+
+ // API query against memory model
+ List allEmployees = (List)pm.newQuery(Employee.class).execute();
+ apiQuery.setCandidates(allEmployees);
+ emps = apiQuery.executeList();
+ checkQueryResultWithoutOrder(ASSERTION_FAILED, SINGLE_STRING_QUERY_LIST_SUBQUERY, emps, expected);
+ } catch (Exception ex) {
+ fail(ASSERTION_FAILED, ex.getLocalizedMessage());
+ }
+ tx.commit();
+ } finally {
+ if (tx.isActive()) {
+ tx.rollback();
+ }
+ }
+ }
+
+ /** */
+ public void testListSingleStringQuery() {
+ Transaction tx = pm.currentTransaction();
+ try {
+ tx.begin();
+ List expected = getTransientCompanyModelInstancesAsList(
+ new String[]{"emp7", "emp8", "emp9", "emp10"});
+ // single String JDOQL
+ try (Query singleStringQuery = pm.newQuery(SINGLE_STRING_QUERY_LIST_SUBQUERY)) {
+ List<FullTimeEmployee> emps = singleStringQuery.executeList();
+ checkQueryResultWithoutOrder(ASSERTION_FAILED, SINGLE_STRING_QUERY_LIST_SUBQUERY, emps, expected);
+ } catch (Exception ex) {
+ fail(ASSERTION_FAILED, ex.getLocalizedMessage());
+ }
+ tx.commit();
+ } finally {
+ if (tx.isActive()) {
+ tx.rollback();
+ }
+ }
+ }
+
+ /** */
+ public void testListJDOQLTypedQuery() {
+ Transaction tx = pm.currentTransaction();
+ try {
+ tx.begin();
+ List expected = getTransientCompanyModelInstancesAsList(
+ new String[]{"emp7", "emp8", "emp9", "emp10"});
+ try (JDOQLTypedQuery<Employee> q = pm.newJDOQLTypedQuery(Employee.class)) {
+ QEmployee cand = QEmployee.candidate();
+ JDOQLTypedSubquery<MeetingRoom> subquery =
+ q.subquery(cand.department.meetingRooms, MeetingRoom.class,"r");
+ QMeetingRoom candsub = QMeetingRoom.candidate("r");
+ q.filter(subquery.selectUnique((NumericExpression)candsub.roomid.max()).eq(3l));
+ List<Employee> emps = q.executeList();
+ checkQueryResultWithoutOrder(ASSERTION_FAILED, SINGLE_STRING_QUERY_COLLECTION_SUBQUERY, emps, expected);
+ } catch (Exception ex) {
+ fail(ASSERTION_FAILED, ex.getLocalizedMessage());
+ }
+ tx.commit();
+ } finally {
+ if (tx.isActive()) {
+ tx.rollback();
+ }
+ }
}
/**
diff --git a/tck/src/main/resources/conf/jdo-signatures.txt b/tck/src/main/resources/conf/jdo-signatures.txt
index cf3399a..2c47436 100644
--- a/tck/src/main/resources/conf/jdo-signatures.txt
+++ b/tck/src/main/resources/conf/jdo-signatures.txt
@@ -1747,8 +1747,6 @@ public interface javax.jdo.JDOQLTypedQuery extends java.io.Serializable, java.io
abstract javax.jdo.JDOQLTypedSubquery subquery(String candidateAlias);
abstract javax.jdo.JDOQLTypedSubquery subquery(Class candidate, String candidateAlias);
abstract javax.jdo.JDOQLTypedSubquery subquery(javax.jdo.query.CollectionExpression candidateCollection, Class candidate , String candidateAlias);
- abstract javax.jdo.JDOQLTypedSubquery subquery(javax.jdo.query.ListExpression candidateList, Class candidate , String candidateAlias);
- abstract javax.jdo.JDOQLTypedSubquery subquery(javax.jdo.query.MapExpression candidateMap, Class candidate , String candidateAlias);
abstract javax.jdo.JDOQLTypedQuery setParameters(java.util.Map namedParamMap);
abstract javax.jdo.JDOQLTypedQuery setParameter(javax.jdo.query.Expression paramExpr, Object value);
abstract javax.jdo.JDOQLTypedQuery setParameter(String paramName, Object value);
diff --git a/tck/src/main/resources/testdata/org/apache/jdo/tck/pc/company/companyForSubqueriesTests.xml b/tck/src/main/resources/testdata/org/apache/jdo/tck/pc/company/companyForSubqueriesTests.xml
index ec710df..0fd109a 100644
--- a/tck/src/main/resources/testdata/org/apache/jdo/tck/pc/company/companyForSubqueriesTests.xml
+++ b/tck/src/main/resources/testdata/org/apache/jdo/tck/pc/company/companyForSubqueriesTests.xml
@@ -41,6 +41,19 @@
</property>
</bean>
+ <bean id="room1" factory-bean="companyFactory" factory-method="newMeetingRoom">
+ <constructor-arg index="0" type="long"><value>1</value></constructor-arg>
+ <constructor-arg index="1" type="java.lang.String"><value>Comfy Room</value></constructor-arg>
+ </bean>
+ <bean id="room2" factory-bean="companyFactory" factory-method="newMeetingRoom">
+ <constructor-arg index="0" type="long"><value>2</value></constructor-arg>
+ <constructor-arg index="1" type="java.lang.String"><value>Large Discussion Room</value></constructor-arg>
+ </bean>
+ <bean id="room3" factory-bean="companyFactory" factory-method="newMeetingRoom">
+ <constructor-arg index="0" type="long"><value>3</value></constructor-arg>
+ <constructor-arg index="1" type="java.lang.String"><value>Conference Room</value></constructor-arg>
+ </bean>
+
<bean id="dept1" factory-bean="companyFactory" factory-method="newDepartment">
<constructor-arg index="0" type="long"><value>1</value></constructor-arg>
<constructor-arg index="1" type="java.lang.String" ><value>Development</value></constructor-arg>
@@ -55,6 +68,12 @@
<ref local="emp6"/>
</set>
</property>
+ <property name="meetingRooms">
+ <list>
+ <ref local="room1"/>
+ <ref local="room2"/>
+ </list>
+ </property>
</bean>
<bean id="dept2" factory-bean="companyFactory" factory-method="newDepartment">
@@ -69,6 +88,11 @@
<ref local="emp10"/>
</set>
</property>
+ <property name="meetingRooms">
+ <list>
+ <ref local="room3"/>
+ </list>
+ </property>
</bean>
<bean id="emp1" factory-bean="companyFactory" factory-method="newFullTimeEmployee">