Search in sources :

Example 1 with CayenneMapEntry

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;
}
Also used : ArcProperty(org.apache.cayenne.reflect.ArcProperty) ObjAttribute(org.apache.cayenne.map.ObjAttribute) ASTDbPath(org.apache.cayenne.exp.parser.ASTDbPath) ClassDescriptor(org.apache.cayenne.reflect.ClassDescriptor) CayenneRuntimeException(org.apache.cayenne.CayenneRuntimeException) DbAttribute(org.apache.cayenne.map.DbAttribute) ToOneProperty(org.apache.cayenne.reflect.ToOneProperty) CayenneMapEntry(org.apache.cayenne.util.CayenneMapEntry) DbEntity(org.apache.cayenne.map.DbEntity) PrefetchTreeNode(org.apache.cayenne.query.PrefetchTreeNode) HashSet(java.util.HashSet) ObjRelationship(org.apache.cayenne.map.ObjRelationship) AttributeProperty(org.apache.cayenne.reflect.AttributeProperty) PrefetchSelectQuery(org.apache.cayenne.query.PrefetchSelectQuery) ObjEntity(org.apache.cayenne.map.ObjEntity) ToManyProperty(org.apache.cayenne.reflect.ToManyProperty) Expression(org.apache.cayenne.exp.Expression) DbRelationship(org.apache.cayenne.map.DbRelationship) DbJoin(org.apache.cayenne.map.DbJoin) PropertyVisitor(org.apache.cayenne.reflect.PropertyVisitor)

Example 2 with CayenneMapEntry

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;
}
Also used : ObjEntity(org.apache.cayenne.map.ObjEntity) CayenneMapEntry(org.apache.cayenne.util.CayenneMapEntry) ObjRelationship(org.apache.cayenne.map.ObjRelationship) CayenneRuntimeException(org.apache.cayenne.CayenneRuntimeException) DbAttribute(org.apache.cayenne.map.DbAttribute)

Example 3 with CayenneMapEntry

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
    }
}
Also used : CayenneMapEntry(org.apache.cayenne.util.CayenneMapEntry) Expression(org.apache.cayenne.exp.Expression) Test(org.junit.Test)

Example 4 with CayenneMapEntry

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);
                    }
                }
            }
        }
    }
}
Also used : CayenneMapEntry(org.apache.cayenne.util.CayenneMapEntry) ObjAttribute(org.apache.cayenne.map.ObjAttribute) DbRelationship(org.apache.cayenne.map.DbRelationship) EntityResultSegment(org.apache.cayenne.query.EntityResultSegment) QuotingStrategy(org.apache.cayenne.dba.QuotingStrategy)

Example 5 with CayenneMapEntry

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());
        }
    }
}
Also used : CayenneMapEntry(org.apache.cayenne.util.CayenneMapEntry)

Aggregations

CayenneMapEntry (org.apache.cayenne.util.CayenneMapEntry)24 ObjEntity (org.apache.cayenne.map.ObjEntity)8 DbEntity (org.apache.cayenne.map.DbEntity)7 DbRelationship (org.apache.cayenne.map.DbRelationship)7 CayenneRuntimeException (org.apache.cayenne.CayenneRuntimeException)6 Expression (org.apache.cayenne.exp.Expression)6 DbAttribute (org.apache.cayenne.map.DbAttribute)6 ObjRelationship (org.apache.cayenne.map.ObjRelationship)5 ObjAttribute (org.apache.cayenne.map.ObjAttribute)4 ASTDbPath (org.apache.cayenne.exp.parser.ASTDbPath)3 Entity (org.apache.cayenne.map.Entity)3 HashSet (java.util.HashSet)2 ObjectId (org.apache.cayenne.ObjectId)2 ExpressionException (org.apache.cayenne.exp.ExpressionException)2 DbJoin (org.apache.cayenne.map.DbJoin)2 AttributeProperty (org.apache.cayenne.reflect.AttributeProperty)2 ClassDescriptor (org.apache.cayenne.reflect.ClassDescriptor)2 PropertyVisitor (org.apache.cayenne.reflect.PropertyVisitor)2 ToManyProperty (org.apache.cayenne.reflect.ToManyProperty)2 ToOneProperty (org.apache.cayenne.reflect.ToOneProperty)2