DataNucleus relationship fetch group confusion - postgresql
All models are here: https://github.com/valentijnscholten/dependency-track/tree/metrics-opt-comp/src/main/java/org/dependencytrack/model
I want to avoid the 1+N queries problem while processing a list of models and accessing a relationship/collection on each of them. This should be easy according to the DataNucleus docs, and is quite common. But it doesn't seem to work as advertised.
When adding the relationship to the fetch group, I can see that DN is doing an extra query to "Bulk Fetch" the relationship. This looks good. But as soon as I actually access the relationship, it does the same query again. But this time specifically for this model instance. So I end up with 1+1+N queries.
Question applies mostly to Component.java:
It has a many-to-many relationship (but I see the same problem with 1-N):
#Persistent(table = "COMPONENTS_VULNERABILITIES")
#Join(column = "COMPONENT_ID")
#Element(column = "VULNERABILITY_ID")
#Order(extensions = #Extension(vendorName = "datanucleus", key = "list-ordering", value = "id ASC"))
private List<Vulnerability> vulnerabilities;
#FetchGroup(name = "METRICS_UPDATE", members = {
#Persistent(name = "id"),
#Persistent(name = "lastInheritedRiskScore"),
#Persistent(name = "uuid"),
#Persistent(name = "vulnerabilities"),
#Persistent(name = "analysises"),
#Persistent(name = "policyViolations")
})
Query<Component> query = pm.newQuery(Component.class);
query.setOrdering("id DESC");
query.setRange(0, 500);
query.getFetchPlan().setGroup(Component.FetchGroup.METRICS_UPDATE.name());
query.getFetchPlan().setFetchSize(FetchPlan.FETCH_SIZE_GREEDY);
query.getFetchPlan().setMaxFetchDepth(10);
components = query.executeList();
LOGGER.debug("Fetched " + components.size() + " components for project " + project.getUuid());
for (final Component component : components) {
try {
LOGGER.debug("Printing vulnerabilities count: " + component.getVulnerabilities().stream().count());
FetchSize and MaxDepth are added because I was trying stuff.
Logs
2023-02-11 18:41:36,458 DEBUG [Query] JDOQL Query : Executing "SELECT FROM org.dependencytrack.model.Component WHERE project == :project ORDER BY id DESC RANGE 0,500" ...
2023-02-11 18:41:36,460 DEBUG [Connection] ManagedConnection OPENED : "org.datanucleus.store.rdbms.ConnectionFactoryImpl$ManagedConnectionImpl#3a5359fd [conn=com.zaxxer.hikari.pool.HikariProxyConnection#78a8a548, commitOnRelease=true, closeOnRelease=true, closeOnTxnEnd=true]" on resource "nontx" with isolation level "read-committed" and auto-commit=false
2023-02-11 18:41:36,460 DEBUG [Datastore] Using PreparedStatement "HikariProxyPreparedStatement#289630101 wrapping SELECT 'org.dependencytrack.model.Component' AS "DN_TYPE","A0"."ID" AS "NUCORDER0","A0"."LAST_RISKSCORE","A0"."UUID" FROM "COMPONENT" "A0" WHERE "A0"."PROJECT_ID" = ? ORDER BY "NUCORDER0" DESC FETCH NEXT 500 ROWS ONLY " for connection "com.zaxxer.hikari.pool.HikariProxyConnection#78a8a548"
2023-02-11 18:41:36,463 DEBUG [Native] SELECT 'org.dependencytrack.model.Component' AS "DN_TYPE","A0"."ID" AS "NUCORDER0","A0"."LAST_RISKSCORE","A0"."UUID" FROM "COMPONENT" "A0" WHERE "A0"."PROJECT_ID" = ? ORDER BY "NUCORDER0" DESC FETCH NEXT 500 ROWS ONLY
2023-02-11 18:41:36,466 DEBUG [Retrieve] SQL Execution Time = 3 ms
2
... some other bulk prefetches snipped
2023-02-11 18:41:36,476 DEBUG [Retrieve] JDOQL Bulk-Fetch of org.dependencytrack.model.Component.vulnerabilities
2023-02-11 18:41:36,476 DEBUG [Datastore] Using PreparedStatement "HikariProxyPreparedStatement#555511245 wrapping SELECT 'org.dependencytrack.model.Vulnerability' AS "DN_TYPE","A1"."CREATED","A1"."CREDITS","A1"."CVSSV2BASESCORE","A1"."CVSSV2EXPLOITSCORE","A1"."CVSSV2IMPACTSCORE","A1"."CVSSV2VECTOR","A1"."CVSSV3BASESCORE","A1"."CVSSV3EXPLOITSCORE","A1"."CVSSV3IMPACTSCORE","A1"."CVSSV3VECTOR","A1"."CWES","A1"."DESCRIPTION","A1"."DETAIL","A1"."EPSSPERCENTILE","A1"."EPSSSCORE","A1"."FRIENDLYVULNID","A1"."ID" AS "NUCORDER0","A1"."OWASPRRBUSINESSIMPACTSCORE","A1"."OWASPRRLIKELIHOODSCORE","A1"."OWASPRRTECHNICALIMPACTSCORE","A1"."OWASPRRVECTOR","A1"."PATCHEDVERSIONS","A1"."PUBLISHED","A1"."RECOMMENDATION","A1"."REFERENCES","A1"."SEVERITY","A1"."SOURCE","A1"."SUBTITLE","A1"."TITLE","A1"."UPDATED","A1"."UUID","A1"."VULNID","A1"."VULNERABLEVERSIONS","A0"."COMPONENT_ID" FROM "COMPONENTS_VULNERABILITIES" "A0" INNER JOIN "VULNERABILITY" "A1" ON "A0"."VULNERABILITY_ID" = "A1"."ID" WHERE EXISTS (SELECT 'org.dependencytrack.model.Component' AS "DN_TYPE","A0_SUB"."ID" AS "DN_APPID" FROM "COMPONENT" "A0_SUB" WHERE "A0_SUB"."PROJECT_ID" = ? AND "A0"."COMPONENT_ID" = "A0_SUB"."ID") ORDER BY "NUCORDER0"" for connection "com.zaxxer.hikari.pool.HikariProxyConnection#78a8a548"
2023-02-11 18:41:36,476 DEBUG [Native] SELECT 'org.dependencytrack.model.Vulnerability' AS "DN_TYPE","A1"."CREATED","A1"."CREDITS","A1"."CVSSV2BASESCORE","A1"."CVSSV2EXPLOITSCORE","A1"."CVSSV2IMPACTSCORE","A1"."CVSSV2VECTOR","A1"."CVSSV3BASESCORE","A1"."CVSSV3EXPLOITSCORE","A1"."CVSSV3IMPACTSCORE","A1"."CVSSV3VECTOR","A1"."CWES","A1"."DESCRIPTION","A1"."DETAIL","A1"."EPSSPERCENTILE","A1"."EPSSSCORE","A1"."FRIENDLYVULNID","A1"."ID" AS "NUCORDER0","A1"."OWASPRRBUSINESSIMPACTSCORE","A1"."OWASPRRLIKELIHOODSCORE","A1"."OWASPRRTECHNICALIMPACTSCORE","A1"."OWASPRRVECTOR","A1"."PATCHEDVERSIONS","A1"."PUBLISHED","A1"."RECOMMENDATION","A1"."REFERENCES","A1"."SEVERITY","A1"."SOURCE","A1"."SUBTITLE","A1"."TITLE","A1"."UPDATED","A1"."UUID","A1"."VULNID","A1"."VULNERABLEVERSIONS","A0"."COMPONENT_ID" FROM "COMPONENTS_VULNERABILITIES" "A0" INNER JOIN "VULNERABILITY" "A1" ON "A0"."VULNERABILITY_ID" = "A1"."ID" WHERE EXISTS (SELECT 'org.dependencytrack.model.Component' AS "DN_TYPE","A0_SUB"."ID" AS "DN_APPID" FROM "COMPONENT" "A0_SUB" WHERE "A0_SUB"."PROJECT_ID" = ? AND "A0"."COMPONENT_ID" = "A0_SUB"."ID") ORDER BY "NUCORDER0"
2023-02-11 18:41:36,480 DEBUG [Retrieve] SQL Execution Time = 4 ms
As far as I can see this is using the filters from the original query to fetch the vulnerabilities relationship. So far so good.
The first statement after the query above is to access getVulnerabilities(), and this results in, more or less, the exact same query:
2023-02-11 18:41:36,494 DEBUG [ProjectMetricsUpdateTask] Fetched 5 components for project f04cfba0-7b94-4380-8cbd-aca492f97a7f
2023-02-11 18:41:36,494 DEBUG [Persistence] Object with id "org.dependencytrack.model.Component:45106" has a lifecycle change : "HOLLOW"->"P_NONTRANS"
2023-02-11 18:41:36,508 DEBUG [Persistence] Object "org.dependencytrack.model.Component#68f46ca4" field "vulnerabilities" is replaced by a SCO wrapper of type "org.datanucleus.store.types.wrappers.backed.List" [cache-values=true, lazy-loading=true, allow-nulls=true]
2023-02-11 18:41:36,508 DEBUG [Persistence] Object "org.dependencytrack.model.Component#68f46ca4" field "vulnerabilities" loading contents to SCO wrapper from the datastore
2023-02-11 18:41:36,512 DEBUG [Connection] ManagedConnection OPENED : "org.datanucleus.store.rdbms.ConnectionFactoryImpl$ManagedConnectionImpl#73aeb4aa [conn=com.zaxxer.hikari.pool.HikariProxyConnection#30f8bfe1, commitOnRelease=true, closeOnRelease=true, closeOnTxnEnd=true]" on resource "nontx" with isolation level "read-committed" and auto-commit=false
2023-02-11 18:41:36,512 DEBUG [Datastore] Using PreparedStatement "HikariProxyPreparedStatement#1310502991 wrapping SELECT 'org.dependencytrack.model.Vulnerability' AS "DN_TYPE","A1"."CREATED","A1"."CREDITS","A1"."CVSSV2BASESCORE","A1"."CVSSV2EXPLOITSCORE","A1"."CVSSV2IMPACTSCORE","A1"."CVSSV2VECTOR","A1"."CVSSV3BASESCORE","A1"."CVSSV3EXPLOITSCORE","A1"."CVSSV3IMPACTSCORE","A1"."CVSSV3VECTOR","A1"."CWES","A1"."DESCRIPTION","A1"."DETAIL","A1"."EPSSPERCENTILE","A1"."EPSSSCORE","A1"."FRIENDLYVULNID","A1"."ID" AS "NUCORDER0","A1"."OWASPRRBUSINESSIMPACTSCORE","A1"."OWASPRRLIKELIHOODSCORE","A1"."OWASPRRTECHNICALIMPACTSCORE","A1"."OWASPRRVECTOR","A1"."PATCHEDVERSIONS","A1"."PUBLISHED","A1"."RECOMMENDATION","A1"."REFERENCES","A1"."SEVERITY","A1"."SOURCE","A1"."SUBTITLE","A1"."TITLE","A1"."UPDATED","A1"."UUID","A1"."VULNID","A1"."VULNERABLEVERSIONS" FROM "COMPONENTS_VULNERABILITIES" "A0" INNER JOIN "VULNERABILITY" "A1" ON "A0"."VULNERABILITY_ID" = "A1"."ID" WHERE "A0"."COMPONENT_ID" = ? ORDER BY "NUCORDER0"" for connection "com.zaxxer.hikari.pool.HikariProxyConnection#30f8bfe1"
2023-02-11 18:41:36,515 DEBUG [Native] SELECT 'org.dependencytrack.model.Vulnerability' AS "DN_TYPE","A1"."CREATED","A1"."CREDITS","A1"."CVSSV2BASESCORE","A1"."CVSSV2EXPLOITSCORE","A1"."CVSSV2IMPACTSCORE","A1"."CVSSV2VECTOR","A1"."CVSSV3BASESCORE","A1"."CVSSV3EXPLOITSCORE","A1"."CVSSV3IMPACTSCORE","A1"."CVSSV3VECTOR","A1"."CWES","A1"."DESCRIPTION","A1"."DETAIL","A1"."EPSSPERCENTILE","A1"."EPSSSCORE","A1"."FRIENDLYVULNID","A1"."ID" AS "NUCORDER0","A1"."OWASPRRBUSINESSIMPACTSCORE","A1"."OWASPRRLIKELIHOODSCORE","A1"."OWASPRRTECHNICALIMPACTSCORE","A1"."OWASPRRVECTOR","A1"."PATCHEDVERSIONS","A1"."PUBLISHED","A1"."RECOMMENDATION","A1"."REFERENCES","A1"."SEVERITY","A1"."SOURCE","A1"."SUBTITLE","A1"."TITLE","A1"."UPDATED","A1"."UUID","A1"."VULNID","A1"."VULNERABLEVERSIONS" FROM "COMPONENTS_VULNERABILITIES" "A0" INNER JOIN "VULNERABILITY" "A1" ON "A0"."VULNERABILITY_ID" = "A1"."ID" WHERE "A0"."COMPONENT_ID" = ? ORDER BY "NUCORDER0"
Disabling L2 Cache doesn't help. There are a couple more relationships in the Component class. They all have the samen problem. I also notice that DN only fetches 1 level of relationships, despite the max depth being set to 10.
So two questions:
Why is the prefetched contents of the relationship not used?
Why is only 1 level of relationships prefetched?
I do notice that the vulnerabilities relation is empty in the database. Could that be triggering the repeat of queries?
Related
Page result returns different values than the generated SQL
After upgrading to spring-data-jpa 3.0.0 a JPQL query that uses Pageable is returning less elements than expected. I executed the generated SQL from the console and that returns the correct number of elements. I don't see a count SQL query being generated when using spring-data-jpa 3.0.0 JPQL Query: #query(value = "select fsd from FeeScheduleDrugEntity fsd " + "left join DrugNdcEntity ndc on fsd.drug.id = ndc.drug.id and fsd.drug.noc = true " + "left join FeeScheduleSourceEntity fsse on fsse.id = fsd.drugFeeScheduleSource.id " + "where fsd.feeSchedule.id = :feeScheduleId ") Generated SQL: select f1_0.fee_schedule_drug_id, f1_0.allowable_per_billing_unit, f1_0.fee_schedule_drug_source, f1_0.created_at, f1_0.created_by, f1_0.drug_id, f1_0.fee_schedule_item_source_id, f1_0.fee_schedule_id, f1_0.modified_at, f1_0.modified_by from core.fee_schedule_drug f1_0 join core.drug d2_0 on d2_0.drug_id = f1_0.drug_id left join core.drug_ndc d1_0 on f1_0.drug_id = d1_0.drug_id and d2_0.is_noc = true left join core.fee_schedule_item_source f2_0 on f2_0.fee_schedule_item_source_id = f1_0.fee_schedule_item_source_id where f1_0.fee_schedule_id=? order by d1_0.ndc asc offset ? rows fetch first ? rows only I am expecting the same number of results from my query
Orientdb sql , select from multiple tables
dont run "select from multiple tables" commands in orientdb 3.0 (centos) i tested like this following commands SELECT * FROM Employee A, City B WHERE A.city = B.id Error Codes ; "Error parsing query: ^ Encountered " "SELECT "" at line 1, column 1. Was expecting one of: ... ... ";" ... DB name="
The most important difference between OrientDB and a Relational Database is that relationships are represented by LINKS instead of JOINs. For this reason, the classic JOIN syntax is not supported. OrientDB uses the "dot (.) notation" to navigate LINKS. Example 1 : In SQL you might create a join such as: SELECT * FROM Employee A, City B WHERE A.city = B.id AND B.name = 'Rome' In OrientDB an equivalent operation would be: SELECT * FROM Employee WHERE city.name = 'Rome' For more information: https://orientdb.com/docs/2.2.x/SQL.html#joins Hope it helps Regards
group by error with postgres and pomm orm
I want to execute the following SQL query : SELECT date, COUNT(id_customers) FROM event WHERE event_id = 3 GROUP BY date When I try this query in my database, it works perfectly. But in my code I get an error which I can't resolve. I use symfony2 with the orm pomm. It's Postgresql. Here is my code : $sql = "SELECT e.date, COUNT(id_customers) FROM event e WHERE event_id = $* GROUP BY e.date"; return $this->query($sql, [$eventId])->extract(); Here is the error : request.CRITICAL: Uncaught PHP Exception InvalidArgumentException: "No such field 'id'. Existing fields are {date, count}" at /home/vagrant/sourcefiles/vendor/pomm-project/model-manager/sources/lib/Model/FlexibleEntity/FlexibleContainer.php line 64 {"exception":" [object] (InvalidArgumentException(code: 0): No such field 'id'. Existing fields are {date, count} at /home/vagrant/sourcefiles/vendor/pomm-project/model-manager/sources/lib/Model/FlexibleEntity/FlexibleContainer.php:64)"} [] So I tried to had the id in my select, by I get this error : request.CRITICAL: Uncaught PHP Exception PommProject\Foundation\Exception\SqlException: " SQL error state '42803' [ERROR] ==== ERROR: column "e.id" must appear in the GROUP BY clause or be used in an aggregate function LINE 1: SELECT e.id, e.date, COUNT(id_customers) FROM event e WHERE ... ^ ==== «PREPARE === SELECT e.id, e.date, COUNT(id_customers) FROM event e WHERE event_id = $1 GROUP BY e.date ===»." at /home/vagrant/sourcefiles/vendor/pomm-project/foundation/sources/lib/Session/Connection.php line 327 {"exception":"[object] (PommProject\Foundation\Exception\SqlException(code: 0): \nSQL error state '42803' [ERROR]\n====\nERROR: column \"e.id\" must appear in the GROUP BY clause or be used in an aggregate function\nLINE 1: SELECT e.id, e.date, COUNT(id_customers) FROM event e WHERE ...\n ^\n\n====\n«PREPARE ===\nSELECT e.id, e.date, COUNT(id_customers) FROM event e WHERE event_id = $1 GROUP BY e.date\n ===». at /home/vagrant/sourcefiles/vendor/pomm-project/foundation/sources/lib/Session/Connection.php:327)"} [] The only thing that works is when I had the id in the group by, but this is not the result I want. Someone can explain me why this is working in the database and not in the php ?
this is because you are fetching flexible entities without their primary key. There is an identity mapper behind the scene that ensure fetching twice the same entity will return the same instance. In this case, you do not need to fetch entities (hence the extract after the query). So you can just use the QueryManager pooler to return converted arrays like the following: $sql = "SELECT e.date, COUNT(id_customers) FROM event e WHERE event_id = $* GROUP BY e.date"; // Return an iterator that fetches converted arrays on demand: return $this ->getSession() ->getQueryManager() ->query($sql, [$eventId]) ;
i think its because the alias, try this $sql = "SELECT e.date, COUNT(e.id_customers) FROM event e WHERE event_id = $* GROUP BY e.date"; return $this->query($sql, [$eventId])->extract();
This is exactly how GROUP BY works in PostgreSQL: When GROUP BY is present, it is not valid for the SELECT list expressions to refer to ungrouped columns except within aggregate functions, since there would be more than one possible value to return for an ungrouped column. It means that each field in your query either must be present in GROUP BY statement or handled by any of the aggregation functions. This is one of the differences between GROUP BY in MySQL and PostreSQL. In other words you can add id at GROUP BY statement and do not worry about it ;)
How to return specific properties from a JPA repository?
I'm getting a ClassCastException when retrieving an entity into a resource. It occurs only when the JPA repository statement explicitly specifies the properties to be returned. For example, this works just fine and there is no exception triggered: #Query("SELECT r FROM Rollout r, RolloutAdmin ra WHERE r.id = ra.rollout.id AND ra.admin = :admin") And it renders as: select rollout0_.id as id1_8_, rollout0_.version as version2_8_, rollout0_.bts_nb_all as bts_nb_a3_8_, rollout0_.country_id as country_9_8_, rollout0_.creation_datetime as creation4_8_, rollout0_.description as descript5_8_, rollout0_.end_datetime as end_date6_8_, rollout0_.name as name7_8_, rollout0_.operator_id as operato10_8_, rollout0_.start_datetime as start_da8_8_ from rollout rollout0_ cross join rollout_admin rolloutadm1_ where rollout0_.id=rolloutadm1_.rollout_id and rolloutadm1_.admin_id=1 order by rollout0_.name asc limit 10; But this triggers the exception: #Query("SELECT DISTINCT r.id, r.country, r.operator, r.name, r.description, r.creationDatetime, r.startDatetime, r.endDatetime FROM Rollout r, RolloutAdmin ra, BTS b WHERE b.rollout.id = r.id AND r.id = ra.rollout.id AND ra.admin = :admin GROUP BY r.id") And it renders as: select distinct rollout0_.id as col_0_0_, rollout0_.country_id as col_1_0_, rollout0_.operator_id as col_2_0_, rollout0_.name as col_3_0_, rollout0_.description as col_4_0_, rollout0_.creation_datetime as col_5_0_, rollout0_.start_datetime as col_6_0_, rollout0_.end_datetime as col_7_0_, country3_.id as id1_4_0_, operator4_.id as id1_6_1_, country3_.version as version2_4_0_, country3_.code as code3_4_0_, country3_.list_order as list_ord4_4_0_, country3_.name as name5_4_0_, operator4_.version as version2_6_1_, operator4_.country_id as country_9_6_1_, operator4_.description as descript3_6_1_, operator4_.image as image4_6_1_, operator4_.messenger as messenge5_6_1_, operator4_.name as name6_6_1_, operator4_.operator_id as operator7_6_1_, operator4_.url as url8_6_1_ from rollout rollout0_ inner join country country3_ on rollout0_.country_id=country3_.id inner join operator operator4_ on rollout0_.operator_id=operator4_.id cross join rollout_admin rolloutadm1_ cross join bts bts2_ where bts2_.rollout_id=rollout0_.id and rollout0_.id=rolloutadm1_.rollout_id and rolloutadm1_.admin_id=1 group by rollout0_.id order by rollout0_.name asc limit 10; How come such a difference ? Any way to specify the properties to be returned without fetching the whole dependent parent properties ? EDIT: I now understand, thanks to Nizet, that I need to return an object of the same type as my repository method.
It's quite simple: SELECT r FROM Rollout r means: I want this query to return r, of type Rollout. The query returns a List<Rollout>. Whereas SELECT DISTINCT r.id, r.country, r.operator, r.name, r.description, r.creationDatetime, r.startDatetime, r.endDatetime FROM Rollout r means: I want this query to return many different things: an ID, a country, an operator, etc. Since the query returns many different things, of different types, and potentially from several different entities (not the case in this specific example, but you could also return properties from ra), the query returns a List<Object[]>, where the first element of the array of objects is ID, the second is country, the third is operator, etc. Not that even your first query is more complex than it should. The association between the two entities already says how the two entities are joined. So all you need is SELECT r FROM RolloutAdmin ra join ra.rollout r WHERE ra.admin = :admin
JPA Criteria Equivalent Query Subquery greatest or max
This SQL query does exactly what is needed when executed. I am looking for the JPA Criteria equivalent? Basically gets the Device entity and the LATEST(greatest) GPS coord from a One-To-Many relationship SELECT DISTINCT t0.DEVICE_I, t0.ACTIVE_S, t0.APP_CONFIG_I, t0.PREV_APP_CONFIG_I, t0.APP_CONFIG_CONFIRMED_S, t0.APP_CONFIG_RECEIPT_D, t0.APP_CONFIG_SENT_S, t0.CHANNEL_X, t0.CREATION_TS, t0.CREATION_USER_I, t0.DEST_QUEUE_C, t0.DIVISION_C, t0.LAN_IP_X, t0.LAST_UPDATE_TS, t0.LAST_UPDATE_USER_I, t0.MOBILE_IP_X, t0.ORIGIN_MP_I, t0.PREV_CHANNEL_X, t0.TELEPHONE_NUMBER_X, t0.VERSION_X, t1.DEVICE_I, t1.COORDINATE_I, t1.CREATION_TS, t1.CREATION_USER_I, t1.GENERATED_TS, t1.GPGGA_X, t1.GPGSA_X, t1.GPGSV_X, t1.GPRMC_X, t1.GPVTG_X, t1.LAST_UPDATE_TS, t1.LAST_UPDATE_USER_I, t1.WORK_ORDER_NUMBER_I FROM DEVICE t0, GPS_COORD t1 WHERE t0.DEVICE_I = t1.DEVICE_I AND t1.GENERATED_TS IN ( select max(GENERATED_TS) from GPS_COORD group by DEVICE_I ) ORDER BY t1.DEVICE_I ASC, t1.GENERATED_TS DESC The java code below is almost there and generates the following query which is missing joined fields needed from GpsCoord. These fields are included if a fetch is done, but the Join is necessary later in the where clause of the Subquery: CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder(); CriteriaQuery<Device> cq = criteriaBuilder.createQuery(Device.class); Root<Device> device = cq.from(Device.class); cq.distinct(true); Join<Device, GpsCoord> j = device.join(Device_.gpsCoords, JoinType.LEFT); //Fetch<Device,GpsCoord> f = device.fetch(Device_.gpsCoords); CriteriaQuery<Device> select = cq.select(device); Subquery<Timestamp> sq = cq.subquery(Timestamp.class); Root<GpsCoord> gpsCoord = sq.from(GpsCoord.class); sq.select(criteriaBuilder.greatest(gpsCoord.get(GpsCoord_.generatedTs))); sq.groupBy(gpsCoord.get(GpsCoord_.device).get(Device_.deviceI)); select.where(j.get(GpsCoord_.generatedTs).in(sq)); TypedQuery<Device> query = this.getEntityManager().createQuery(cq); Query that is generated is missing the GpsCoord fileds from the Join oepration SELECT DISTINCT t0.DEVICE_I, t0.ACTIVE_S, t0.APP_CONFIG_I, t0.PREV_APP_CONFIG_I, t0.APP_CONFIG_CONFIRMED_S, t0.APP_CONFIG_RECEIPT_D, t0.APP_CONFIG_SENT_S, t0.CHANNEL_X, t0.CREATION_TS, t0.CREATION_USER_I, t0.DEST_QUEUE_C, t0.DIVISION_C, t0.LAN_IP_X, t0.LAST_UPDATE_TS, t0.LAST_UPDATE_USER_I, t0.MOBILE_IP_X, t0.ORIGIN_MP_I, t0.PREV_CHANNEL_X, t0.TELEPHONE_NUMBER_X, t0.VERSION_X FROM SPW_OWN.DEVICE t0, SPW_OWN.GPS_COORD t1 WHERE (t1.GENERATED_TS IN (SELECT MAX(t2.GENERATED_TS) FROM SPW_OWN.GPS_COORD t2, SPW_OWN.DEVICE t3 WHERE t2.DEVICE_I = t3.DEVICE_I GROUP BY t3.DEVICE_I)) AND t0.DEVICE_I = t1.DEVICE_I(+) Using a multiselect from the CriteriaQuery like this generates the proper SQL when executed, but the following exception Caused by: java.lang.Exception: java.lang.RuntimeException: Can not find constructor for "class Device" with argument types "[class java.lang.String ... java.sql.Timestamp]" to fill data. gets thrown after the call: CriteriaQuery<Device> select = cq.multiselect( //"deviceI", device.get(Device_.deviceI), //"activeS", device.get(Device_.activeS), //"appConfigConfirmedS", device.get(Device_.appConfigConfirmedS), //"appConfigReceiptD", device.get(Device_.appConfigReceiptD), //"appConfigSentS", device.get(Device_.appConfigSentS), //"channelX", device.get(Device_.channelX), //"destQueueC", device.get(Device_.destQueueC), //"lanIpX", device.get(Device_.lanIpX), //"mobileIpX", device.get(Device_.mobileIpX), //"prevChannelX", device.get(Device_.prevChannelX), //"telephoneNumberX", device.get(Device_.telephoneNumberX), //"versionX" device.get(Device_.versionX), //"device", j.get(GpsCoord_.device), //"gpggaX", j.get(GpsCoord_.gpggaX), //"gprmcX", j.get(GpsCoord_.gprmcX), //"gpgsaX", j.get(GpsCoord_.gpgsaX), //"gpgsvX", j.get(GpsCoord_.gpgsvX), //"gpvtgX" j.get(GpsCoord_.gpvtgX), j.get(GpsCoord_.generatedTs) );
A few issues, you call, sq.from(GpsCoord.class); twice using device.fetch(Device_.gpsCoords); will also fetch the object, and will join it twice you IN is incorrect it should be, select.where(criteriaBuilder.get(path).in(sq));