Hi I discovered a problem with joint prefetch and flattened attributes. This is in 3.1B2 which is the version we use at the moment. Not sure if this is the lates or not..
If I have a entity A with a normal relationship to an entity B, and B has flattened attributes from C via toCdbrel.SOME_PROPERTY. This works fine normally. But when using joint prefetch the table aliases in the query will be wrong. (Querying A and prefetching B). If there are multiple flattened attributes, C will also be joined in multiple times. The problem is in SelectTranslator#appendQueryColumns. This section adds columns from the target entity of a joint prefetch, including relationships for flattened attributes: while (targetObjAttrs.hasNext()) { ObjAttribute oa = targetObjAttrs.next(); Iterator<CayenneMapEntry> dbPathIterator = oa.getDbPathIterator(); while (dbPathIterator.hasNext()) { Object pathPart = dbPathIterator.next(); if (pathPart == null) { throw new CayenneRuntimeException( "ObjAttribute has no component: " + oa.getName()); } else if (pathPart instanceof DbRelationship) { DbRelationship rel = (DbRelationship) pathPart; dbRelationshipAdded(rel, JoinType.INNER, null); //PROBLEM } else if (pathPart instanceof DbAttribute) { DbAttribute attribute = (DbAttribute) pathPart; appendColumn(columns, oa, attribute, attributes, labelPrefix + '.' + attribute.getName()); } } } The problem is that dbRelationshipAdded sets the topNode of the JoinStack to the new node when a relationship is added and this is never reset in this case. So every time a relationship is added in this loop it will add a node to the previously added node. This will increase the tableAlias by 1 for every relationship, and also cause any attributes directly on B to belong to the wrong node and therefore get the wrong tableAlias. It will also cause an additional join, but joined to the wrong table. The topNode of the JoinStack needs to be reset to the correct value after handing each attribute, or more presisely: after the "while (dbPathIterator.hasNext())" loop the topNode needs to be the same as it was before the loop, because we are still processing attributes on the same entity (B in this case). Storing the current topNode, and resetting it after processing each attribute fixed the problem for me. Like this: while (targetObjAttrs.hasNext()) { JoinTreeNode topNode = getJoinStack().topNode; // FIX ObjAttribute oa = targetObjAttrs.next(); Iterator<CayenneMapEntry> dbPathIterator = oa.getDbPathIterator(); while (dbPathIterator.hasNext()) { Object pathPart = dbPathIterator.next(); if (pathPart == null) { throw new CayenneRuntimeException( "ObjAttribute has no component: " + oa.getName()); } else if (pathPart instanceof DbRelationship) { DbRelationship rel = (DbRelationship) pathPart; dbRelationshipAdded(rel, JoinType.INNER, null); } else if (pathPart instanceof DbAttribute) { DbAttribute attribute = (DbAttribute) pathPart; appendColumn(columns, oa, attribute, attributes, labelPrefix + '.' + attribute.getName()); } } getJoinStack().topNode = topNode; // FIX } resetJoinStack() can't be used in this case, since we don't want the rootNode. I've run the Cayenne tests after this change with no errors, but I've only done limited testing in our own app so far. This is clearly a bug. Should I create a JIRA issue? – Erlend <http://www.dataloy-systems.com>