| Summary: | Even if Bug 356296 is fixed, there are problems with the QueryHints.BATCH hint - the root expression on a path to a batch query is also changed to a batch query | ||
|---|---|---|---|
| Product: | z_Archived | Reporter: | Martin Marinschek <martin.marinschek> |
| Component: | Eclipselink | Assignee: | Project Inbox <eclipselink.orm-inbox> |
| Status: | NEW --- | QA Contact: | |
| Severity: | major | ||
| Priority: | P2 | CC: | jamesssss, michaelnielson, tom.ware |
| Version: | unspecified | ||
| Target Milestone: | --- | ||
| Hardware: | PC | ||
| OS: | Windows XP | ||
| Whiteboard: | |||
Additional thoughts: it might be reasonable (I don't know why, but you might have reasons) to load everything on a path to a batch expression with a batch, but why then only the root expression part? Plus, additionally, if stuff is loaded via a batch expression, it should at least not be part of the main query - but indeed it is still joined in. Right now, stuff is loaded twice. And even twice is not entirely correct: from the logs, it seems to me that the batch query is executed per row, with exactly the same ids (although shuffled up between the rows) all over again. best regards, Martin Some more thoughts on this issue:
while (!baseExpression.getBaseExpression().isExpressionBuilder()) {
baseExpression =
(ObjectExpression)baseExpression.getBaseExpression();
}
I wonder why this code is there and when the loop should break. In my case, it breaks at the root expression, which has a left join QueryHint associcated with it. I don't think that is what was intended. It might be intended to stop at the first expression which is directly reached by the query. Could this be what the author of the code really intended?
best regards,
Martin
I have debugged some more on this, and prepared a patch that works for me (but won't work for all general cases). Right now in Eclipselink, as far as I can tell, Batch queries will only work on the second level - further down, they will just not work. I guess you guys need more test cases to cover more complex queries. best regards, Martin Setting target and priority. See the following page for the meanings of these fields: http://wiki.eclipse.org/EclipseLink/Development/Bugs/Guidelines Community: Please vote for this bug if it is important to you. Votes are one of the main criteria we use to determine which bugs to fix next. (In reply to comment #3) > I have debugged some more on this, and prepared a patch that works for me > (but won't work for all general cases). > > Right now in Eclipselink, as far as I can tell, Batch queries will only work > on the second level - further down, they will just not work. I guess you > guys need more test cases to cover more complex queries. > > best regards, > > Martin I know it's been quite a while but if you have the partial patch I'd be interested in seeing it? I'm not associated with EclipseLink, I've just been working through a few Batch Fetch issues. Cheers! Michael Nielson Please list your use cases that do not work, and what your expected behavior is. Are you joining as well as batching? You cannot batch a nested child without batching the path, otherwise you will not be batching anything. The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink |
Build Identifier: 2.2.1 The following code changes the root expression on the path to a batch query also to a batch query: ObjectLevelReadQuery: /** * INTERNAL: * This method is used when computing the nested queries for batch read mappings. * It recurses computing the nested mapping queries. */ protected void computeNestedQueriesForBatchReadExpressions(List<Expression> batchReadExpressions) { int size = batchReadExpressions.size(); for (int index = 0; index < size; index++) { ObjectExpression objectExpression = (ObjectExpression)batchReadExpressions.get(index); // Expression may not have been initialized. ExpressionBuilder builder = objectExpression.getBuilder(); builder.setSession(getSession().getRootSession(null)); builder.setQueryClass(getReferenceClass()); // PERF: Cache join attribute names. ObjectExpression baseExpression = objectExpression; while (!baseExpression.getBaseExpression().isExpressionBuilder()) { baseExpression = (ObjectExpression)baseExpression.getBaseExpression(); } this.batchFetchPolicy.getAttributes().add(baseExpression.getName()); // Ignore nested if (objectExpression.getBaseExpression().isExpressionBuilder()) { DatabaseMapping mapping = objectExpression.getMapping(); if ((mapping != null) && mapping.isForeignReferenceMapping()) { // A nested query must be built to pass to the descriptor that looks like the real query execution would. ReadQuery nestedQuery = ((ForeignReferenceMapping)mapping).prepareNestedBatchQuery(this); // Register the nested query to be used by the mapping for all the objects. this.batchFetchPolicy.getMappingQueries().put(mapping, nestedQuery); } } } } Here, instead of just adding the attributes as they were passed in the QueryHint, the base expression is retrieved and the base expression name is registered. Later on, by: ForeignReferenceMapping#returnValueFromRow: // PERF: Direct variable access. // If the query uses batch reading, return a special value holder // or retrieve the object from the query property. if (sourceQuery.isObjectLevelReadQuery() && (((ObjectLevelReadQuery)sourceQuery).isAttributeBatchRead(this.descriptor, getAttributeName()) || (sourceQuery.isReadAllQuery() && shouldUseBatchReading()))) { return batchedValueFromRow(row, (ObjectLevelReadQuery)sourceQuery, cacheKey); } and subsequently: ObjectLevelReadQuery: /** * INTERNAL: * Return if the attribute is specified for batch reading. */ public boolean isAttributeBatchRead(ClassDescriptor mappingDescriptor, String attributeName) { if (this.batchFetchPolicy == null) { return false; } // Since aggregates share the same query as their parent, must avoid the aggregate thinking // the parents mappings is for it, (queries only share if the aggregate was not joined). if (mappingDescriptor.isAggregateDescriptor() && (mappingDescriptor != this.descriptor)) { return false; } return this.batchFetchPolicy.isAttributeBatchRead(mappingDescriptor, attributeName); } the base expression name is retrieved as batch-enabled. I am scratching my head as to why this would be a good idea, but maybe I am missing an important concept? Suggested patch: don't queue the base expression attribute name, instead just queue the attribute name the user registered as batch enabled - that should be it. best regards, Martin Reproducible: Always