use of org.apache.cayenne.util.CayenneMapEntry in project cayenne by apache.
the class DefaultSelectTranslator method appendQueryColumns.
/**
* Appends columns needed for object SelectQuery to the provided columns
* list.
*/
<T> List<ColumnDescriptor> appendQueryColumns(final List<ColumnDescriptor> columns, SelectQuery<T> query, ClassDescriptor descriptor, final String tableAlias) {
final Set<ColumnTracker> attributes = new HashSet<>();
// fetched attributes include attributes that are either:
//
// * class properties
// * PK
// * FK used in relationship
// * joined prefetch PK
ObjEntity oe = descriptor.getEntity();
PropertyVisitor visitor = new PropertyVisitor() {
public boolean visitAttribute(AttributeProperty property) {
ObjAttribute oa = property.getAttribute();
resetJoinStack();
Iterator<CayenneMapEntry> dbPathIterator = oa.getDbPathIterator();
while (dbPathIterator.hasNext()) {
Object pathPart = dbPathIterator.next();
if (pathPart == null) {
throw new CayenneRuntimeException("ObjAttribute has no component: %s", oa.getName());
} else if (pathPart instanceof DbRelationship) {
DbRelationship rel = (DbRelationship) pathPart;
dbRelationshipAdded(rel, JoinType.LEFT_OUTER, null);
} else if (pathPart instanceof DbAttribute) {
DbAttribute dbAttr = (DbAttribute) pathPart;
appendColumn(columns, oa, dbAttr, attributes, null, tableAlias);
}
}
return true;
}
public boolean visitToMany(ToManyProperty property) {
visitRelationship(property);
return true;
}
public boolean visitToOne(ToOneProperty property) {
visitRelationship(property);
return true;
}
private void visitRelationship(ArcProperty property) {
resetJoinStack();
ObjRelationship rel = property.getRelationship();
DbRelationship dbRel = rel.getDbRelationships().get(0);
List<DbJoin> joins = dbRel.getJoins();
for (DbJoin join : joins) {
DbAttribute src = join.getSource();
appendColumn(columns, null, src, attributes, null, tableAlias);
}
}
};
descriptor.visitAllProperties(visitor);
// stack should be reset, because all root table attributes go with "t0"
// table alias
resetJoinStack();
// add remaining needed attrs from DbEntity
DbEntity table = oe.getDbEntity();
for (DbAttribute dba : table.getPrimaryKeys()) {
appendColumn(columns, null, dba, attributes, null, tableAlias);
}
if (query instanceof PrefetchSelectQuery) {
// for each relationship path add PK of the target entity...
for (String path : ((PrefetchSelectQuery) query).getResultPaths()) {
ASTDbPath pathExp = (ASTDbPath) oe.translateToDbPath(ExpressionFactory.exp(path));
// add joins and find terminating element
resetJoinStack();
PathComponent<DbAttribute, DbRelationship> lastComponent = null;
for (PathComponent<DbAttribute, DbRelationship> component : table.resolvePath(pathExp, getPathAliases())) {
if (component.getRelationship() != null) {
// do not invoke dbRelationshipAdded(), invoke
// pushJoin() instead. This is to prevent
// 'forcingDistinct' flipping to true, that will result
// in unneeded extra processing and sometimes in invalid
// results (see CAY-1979). Distinctness of each row is
// guaranteed by the prefetch query semantics - we
// include target ID in the result columns
getJoinStack().pushJoin(component.getRelationship(), component.getJoinType(), null);
}
lastComponent = component;
}
// process terminating element
if (lastComponent != null) {
DbRelationship relationship = lastComponent.getRelationship();
if (relationship != null) {
String labelPrefix = pathExp.getPath();
DbEntity targetEntity = relationship.getTargetEntity();
for (DbAttribute pk : targetEntity.getPrimaryKeys()) {
// note that we my select a source attribute, but
// label it as
// target for simplified snapshot processing
appendColumn(columns, null, pk, attributes, labelPrefix + '.' + pk.getName());
}
}
}
}
}
// handle joint prefetches directly attached to this query...
if (query.getPrefetchTree() != null) {
// perform some sort of union or sub-queries.
for (PrefetchTreeNode prefetch : query.getPrefetchTree().getChildren()) {
prefetch.setEntityName(oe.getName());
}
for (PrefetchTreeNode prefetch : query.getPrefetchTree().adjacentJointNodes()) {
// for each prefetch add all joins plus columns from the target
// entity
Expression prefetchExp = ExpressionFactory.exp(prefetch.getPath());
ASTDbPath dbPrefetch = (ASTDbPath) oe.translateToDbPath(prefetchExp);
resetJoinStack();
DbRelationship r = null;
for (PathComponent<DbAttribute, DbRelationship> component : table.resolvePath(dbPrefetch, getPathAliases())) {
r = component.getRelationship();
dbRelationshipAdded(r, JoinType.LEFT_OUTER, null);
}
if (r == null) {
throw new CayenneRuntimeException("Invalid joint prefetch '%s' for entity: %s", prefetch, oe.getName());
}
// add columns from the target entity, including those that are matched
// against the FK of the source entity.
// This is needed to determine whether optional relationships are null
// go via target OE to make sure that Java types are mapped correctly...
ObjRelationship targetRel = (ObjRelationship) prefetchExp.evaluate(oe);
ObjEntity targetEntity = targetRel.getTargetEntity();
String labelPrefix = dbPrefetch.getPath();
PropertyVisitor prefetchVisitor = new PropertyVisitor() {
public boolean visitAttribute(AttributeProperty property) {
ObjAttribute oa = property.getAttribute();
Iterator<CayenneMapEntry> dbPathIterator = oa.getDbPathIterator();
while (dbPathIterator.hasNext()) {
Object pathPart = dbPathIterator.next();
if (pathPart == null) {
throw new CayenneRuntimeException("ObjAttribute has no component: %s", oa.getName());
} else if (pathPart instanceof DbRelationship) {
DbRelationship rel = (DbRelationship) pathPart;
dbRelationshipAdded(rel, JoinType.INNER, null);
} else if (pathPart instanceof DbAttribute) {
DbAttribute dbAttr = (DbAttribute) pathPart;
appendColumn(columns, oa, dbAttr, attributes, labelPrefix + '.' + dbAttr.getName());
}
}
return true;
}
public boolean visitToMany(ToManyProperty property) {
return true;
}
public boolean visitToOne(ToOneProperty property) {
return true;
}
};
ClassDescriptor prefetchClassDescriptor = entityResolver.getClassDescriptor(targetEntity.getName());
prefetchClassDescriptor.visitAllProperties(prefetchVisitor);
// append remaining target attributes such as keys
DbEntity targetDbEntity = r.getTargetEntity();
for (DbAttribute attribute : targetDbEntity.getAttributes()) {
appendColumn(columns, null, attribute, attributes, labelPrefix + '.' + attribute.getName());
}
}
}
return columns;
}
use of org.apache.cayenne.util.CayenneMapEntry in project cayenne by apache.
the class ASTDbIdPath method evaluateEntityNode.
@Override
protected CayenneMapEntry evaluateEntityNode(Entity entity) {
int lastDot = path.lastIndexOf('.');
String objPath = null;
String id = path;
if (lastDot > -1) {
objPath = path.substring(0, lastDot);
id = path.substring(lastDot + 1);
}
if (!(entity instanceof ObjEntity)) {
throw new CayenneRuntimeException("Unable to evaluate DBID path for DbEntity");
}
ObjEntity objEntity = (ObjEntity) entity;
if (objPath != null) {
CayenneMapEntry entry = new ASTObjPath(objPath).evaluateEntityNode(objEntity);
if (!(entry instanceof ObjRelationship)) {
throw new CayenneRuntimeException("Unable to evaluate DBID path %s, relationship expected", path);
}
objEntity = ((ObjRelationship) entry).getTargetEntity();
}
DbAttribute pk = objEntity.getDbEntity().getAttribute(id);
if (pk == null || !pk.isPrimaryKey()) {
throw new CayenneRuntimeException("Unable to find PK %s for entity %s", id, objEntity.getName());
}
return pk;
}
use of org.apache.cayenne.util.CayenneMapEntry in project cayenne by apache.
the class EntityIT method testResolveBadObjPath1.
@Test
public void testResolveBadObjPath1() {
// test invalid expression path
Expression pathExpr = ExpressionFactory.expressionOfType(Expression.OBJ_PATH);
pathExpr.setOperand(0, "invalid.invalid");
// itertator should be returned, but when trying to read 1st component,
// it should throw an exception....
ObjEntity galleryEnt = runtime.getDataDomain().getEntityResolver().getObjEntity("Gallery");
Iterator<CayenneMapEntry> it = galleryEnt.resolvePathComponents(pathExpr);
assertTrue(it.hasNext());
try {
it.next();
fail();
} catch (Exception ex) {
// exception expected
}
}
use of org.apache.cayenne.util.CayenneMapEntry in project cayenne by apache.
the class EJBQLJoinAppender method generateJoinsForFlattenedAttributes.
/**
* Generates Joins statements for those flattened attributes that appear after the
* FROM clause, e.g. in WHERE, ORDER BY, etc clauses. Flattened attributes of the
* entity from the SELECT clause are processed earlier and therefore are omitted.
*
* @param id table to JOIN id
*/
private void generateJoinsForFlattenedAttributes(EJBQLTableId id) {
String entityName = context.getEntityDescriptor(id.getEntityId()).getEntity().getName();
// if the dbPath is not null, all attributes of the entity are processed earlier
boolean isProcessingOmitted = id.getDbPath() != null;
String sourceExpression = context.getCompiledExpression().getSource();
List<Object> resultSetMapping = context.getMetadata().getResultSetMapping();
for (Object mapping : resultSetMapping) {
if (mapping instanceof EntityResultSegment) {
if (entityName.equals(((EntityResultSegment) mapping).getClassDescriptor().getEntity().getName())) {
// if entity is included into SELECT clause, all its attributes are processed earlier
isProcessingOmitted = true;
break;
}
}
}
if (!isProcessingOmitted) {
QuotingStrategy quoter = context.getQuotingStrategy();
Collection<ObjAttribute> attributes = context.getEntityDescriptor(id.getEntityId()).getEntity().getAttributes();
for (ObjAttribute objAttribute : attributes) {
if (objAttribute.isFlattened() && sourceExpression.contains(id.getEntityId() + "." + objAttribute.getName())) {
// joins for attribute are generated if it is flattened and appears in original statement
Iterator<CayenneMapEntry> dbPathIterator = objAttribute.getDbPathIterator();
while (dbPathIterator.hasNext()) {
CayenneMapEntry next = dbPathIterator.next();
if (next instanceof DbRelationship) {
DbRelationship rel = (DbRelationship) next;
context.append(" LEFT OUTER JOIN ");
String targetEntityName = quoter.quotedFullyQualifiedName(rel.getTargetEntity());
String subqueryTargetAlias = context.getTableAlias(id.getEntityId(), targetEntityName);
context.append(targetEntityName).append(' ').append(subqueryTargetAlias);
generateJoiningExpression(rel, context.getTableAlias(id.getEntityId(), quoter.quotedFullyQualifiedName(rel.getSourceEntity())), subqueryTargetAlias);
}
}
}
}
}
}
use of org.apache.cayenne.util.CayenneMapEntry in project cayenne by apache.
the class MappingCache method checkNameDuplicates.
/**
* Utility method to warn about name duplicates in different DataMaps.
*
* @param src map
* @param dst map with already added entities
* @param srcMap source DataMap
*/
private void checkNameDuplicates(Map<String, ? extends CayenneMapEntry> src, Map<String, ? extends CayenneMapEntry> dst, DataMap srcMap) {
for (CayenneMapEntry entry : src.values()) {
CayenneMapEntry duplicate = dst.get(entry.getName());
if (duplicate != null) {
DataMap parent = (DataMap) duplicate.getParent();
logger.warn("Found duplicated name " + entry.getName() + " in datamaps " + srcMap.getName() + " and " + parent.getName());
}
}
}
Aggregations