Search in sources :

Example 1 with DbJoin

use of org.apache.cayenne.map.DbJoin 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 DbJoin

use of org.apache.cayenne.map.DbJoin in project cayenne by apache.

the class JoinStack method appendJoinSubtree.

protected void appendJoinSubtree(StringBuilder out, JoinTreeNode node) {
    DbRelationship relationship = node.getRelationship();
    DbEntity targetEntity = relationship.getTargetEntity();
    String srcAlias = node.getSourceTableAlias();
    String targetAlias = node.getTargetTableAlias();
    switch(node.getJoinType()) {
        case INNER:
            out.append(" JOIN");
            break;
        case LEFT_OUTER:
            out.append(" LEFT JOIN");
            break;
        default:
            throw new IllegalArgumentException("Unsupported join type: " + node.getJoinType());
    }
    out.append(' ');
    out.append(quotingStrategy.quotedFullyQualifiedName(targetEntity));
    out.append(' ');
    out.append(quotingStrategy.quotedIdentifier(targetEntity, targetAlias));
    out.append(" ON (");
    List<DbJoin> joins = relationship.getJoins();
    int len = joins.size();
    for (int i = 0; i < len; i++) {
        DbJoin join = joins.get(i);
        if (i > 0) {
            out.append(" AND ");
        }
        out.append(quotingStrategy.quotedIdentifier(relationship.getSourceEntity(), srcAlias, join.getSourceName()));
        out.append(" = ");
        out.append(quotingStrategy.quotedIdentifier(targetEntity, targetAlias, join.getTargetName()));
    }
    /*
		 * Attaching root Db entity's qualifier
		 */
    Expression dbQualifier = targetEntity.getQualifier();
    if (dbQualifier != null) {
        dbQualifier = dbQualifier.transform(new JoinedDbEntityQualifierTransformer(node));
        if (len > 0) {
            out.append(" AND ");
        }
        qualifierTranslator.setOut(out);
        qualifierTranslator.doAppendPart(dbQualifier);
    }
    out.append(')');
    for (JoinTreeNode child : node.getChildren()) {
        appendJoinSubtree(out, child);
    }
}
Also used : DbEntity(org.apache.cayenne.map.DbEntity) Expression(org.apache.cayenne.exp.Expression) DbRelationship(org.apache.cayenne.map.DbRelationship) DbJoin(org.apache.cayenne.map.DbJoin)

Example 3 with DbJoin

use of org.apache.cayenne.map.DbJoin in project cayenne by apache.

the class OpenBaseJoinStack method appendQualifierSubtree.

protected void appendQualifierSubtree(StringBuilder out, JoinTreeNode node) {
    DbRelationship relationship = node.getRelationship();
    String srcAlias = node.getSourceTableAlias();
    String targetAlias = node.getTargetTableAlias();
    List<DbJoin> joins = relationship.getJoins();
    int len = joins.size();
    for (int i = 0; i < len; i++) {
        DbJoin join = joins.get(i);
        if (i > 0) {
            out.append(" AND ");
        }
        out.append(srcAlias).append('.').append(join.getSourceName());
        switch(node.getJoinType()) {
            case INNER:
                out.append(" = ");
                break;
            case LEFT_OUTER:
                out.append(" * ");
                break;
            default:
                throw new IllegalArgumentException("Unsupported join type: " + node.getJoinType());
        }
        out.append(targetAlias).append('.').append(join.getTargetName());
    }
    for (JoinTreeNode child : node.getChildren()) {
        out.append(" AND ");
        appendQualifierSubtree(out, child);
    }
}
Also used : DbRelationship(org.apache.cayenne.map.DbRelationship) DbJoin(org.apache.cayenne.map.DbJoin) JoinTreeNode(org.apache.cayenne.access.translator.select.JoinTreeNode)

Example 4 with DbJoin

use of org.apache.cayenne.map.DbJoin in project cayenne by apache.

the class BaseDataObject method validateForSave.

/**
 * Performs property validation of the object, appending any validation
 * failures to the provided validationResult object. This method is invoked
 * from "validateFor.." before committing a NEW or MODIFIED object to the
 * database. Validation includes checking for null values and value sizes.
 * CayenneDataObject subclasses may override this method, calling super.
 *
 * @since 1.1
 */
protected void validateForSave(ValidationResult validationResult) {
    ObjEntity objEntity = getObjectContext().getEntityResolver().getObjEntity(this);
    if (objEntity == null) {
        throw new CayenneRuntimeException("No ObjEntity mapping found for DataObject %s", getClass().getName());
    }
    // validate mandatory attributes
    Map<String, ValidationFailure> failedDbAttributes = null;
    for (ObjAttribute next : objEntity.getAttributes()) {
        // TODO: andrus, 2/20/2007 - handle embedded attribute
        if (next instanceof EmbeddedAttribute) {
            continue;
        }
        DbAttribute dbAttribute = next.getDbAttribute();
        if (dbAttribute == null) {
            throw new CayenneRuntimeException("ObjAttribute '%s" + "' does not have a corresponding DbAttribute", next.getName());
        }
        // pk may still be generated
        if (dbAttribute.isPrimaryKey()) {
            continue;
        }
        Object value = this.readPropertyDirectly(next.getName());
        if (dbAttribute.isMandatory()) {
            ValidationFailure failure = BeanValidationFailure.validateNotNull(this, next.getName(), value);
            if (failure != null) {
                if (failedDbAttributes == null) {
                    failedDbAttributes = new HashMap<>();
                }
                failedDbAttributes.put(dbAttribute.getName(), failure);
                continue;
            }
        }
        // validate length
        if (value != null && dbAttribute.getMaxLength() > 0) {
            if (value.getClass().isArray()) {
                int len = Array.getLength(value);
                if (len > dbAttribute.getMaxLength()) {
                    String message = "\"" + next.getName() + "\" exceeds maximum allowed length (" + dbAttribute.getMaxLength() + " bytes): " + len;
                    validationResult.addFailure(new BeanValidationFailure(this, next.getName(), message));
                }
            } else if (value instanceof CharSequence) {
                int len = ((CharSequence) value).length();
                if (len > dbAttribute.getMaxLength()) {
                    String message = "\"" + next.getName() + "\" exceeds maximum allowed length (" + dbAttribute.getMaxLength() + " chars): " + len;
                    validationResult.addFailure(new BeanValidationFailure(this, next.getName(), message));
                }
            }
        }
    }
    // validate mandatory relationships
    for (final ObjRelationship relationship : objEntity.getRelationships()) {
        List<DbRelationship> dbRels = relationship.getDbRelationships();
        if (dbRels.isEmpty()) {
            continue;
        }
        // see ObjRelationship.recalculateReadOnlyValue() for more info
        if (relationship.isSourceIndependentFromTargetChange()) {
            continue;
        }
        // if db relationship is not based on a PK and is based on mandatory
        // attributes, see if we have a target object set
        // relationship will be validated only if all db path has mandatory
        // db relationships
        boolean validate = true;
        for (DbRelationship dbRelationship : dbRels) {
            for (DbJoin join : dbRelationship.getJoins()) {
                DbAttribute source = join.getSource();
                if (source.isMandatory()) {
                    // clear attribute failures...
                    if (failedDbAttributes != null && !failedDbAttributes.isEmpty()) {
                        failedDbAttributes.remove(source.getName());
                    }
                } else {
                    // do not validate if the relation is based on
                    // multiple keys with some that can be nullable.
                    validate = false;
                }
            }
        }
        if (validate) {
            Object value = this.readPropertyDirectly(relationship.getName());
            ValidationFailure failure = BeanValidationFailure.validateNotNull(this, relationship.getName(), value);
            if (failure != null) {
                validationResult.addFailure(failure);
            }
        }
    }
    // deal with previously found attribute failures...
    if (failedDbAttributes != null && !failedDbAttributes.isEmpty()) {
        for (ValidationFailure failure : failedDbAttributes.values()) {
            validationResult.addFailure(failure);
        }
    }
}
Also used : ObjRelationship(org.apache.cayenne.map.ObjRelationship) ObjAttribute(org.apache.cayenne.map.ObjAttribute) BeanValidationFailure(org.apache.cayenne.validation.BeanValidationFailure) DbAttribute(org.apache.cayenne.map.DbAttribute) EmbeddedAttribute(org.apache.cayenne.map.EmbeddedAttribute) ValidationFailure(org.apache.cayenne.validation.ValidationFailure) BeanValidationFailure(org.apache.cayenne.validation.BeanValidationFailure) ObjEntity(org.apache.cayenne.map.ObjEntity) DbRelationship(org.apache.cayenne.map.DbRelationship) DbJoin(org.apache.cayenne.map.DbJoin)

Example 5 with DbJoin

use of org.apache.cayenne.map.DbJoin in project cayenne by apache.

the class EOModelProcessor method makeRelationships.

/**
 * Create ObjRelationships of the specified entity, as well as
 * DbRelationships of the corresponding DbEntity.
 */
protected void makeRelationships(EOModelHelper helper, ObjEntity objEntity) {
    Map entityPlistMap = helper.entityPListMap(objEntity.getName());
    List classProps = (List) entityPlistMap.get("classProperties");
    List rinfo = (List) entityPlistMap.get("relationships");
    Collection attributes = (Collection) entityPlistMap.get("attributes");
    if (rinfo == null) {
        return;
    }
    if (classProps == null) {
        classProps = Collections.EMPTY_LIST;
    }
    if (attributes == null) {
        attributes = Collections.EMPTY_LIST;
    }
    DbEntity dbSrc = objEntity.getDbEntity();
    Iterator it = rinfo.iterator();
    while (it.hasNext()) {
        Map relMap = (Map) it.next();
        String targetName = (String) relMap.get("destination");
        // ignore flattened relationships for now
        if (targetName == null) {
            continue;
        }
        String relName = (String) relMap.get("name");
        boolean toMany = "Y".equals(relMap.get("isToMany"));
        boolean toDependentPK = "Y".equals(relMap.get("propagatesPrimaryKey"));
        ObjEntity target = helper.getDataMap().getObjEntity(targetName);
        // ignoring those now.
        if (target == null) {
            continue;
        }
        DbEntity dbTarget = target.getDbEntity();
        Map targetPlistMap = helper.entityPListMap(targetName);
        Collection targetAttributes = (Collection) targetPlistMap.get("attributes");
        DbRelationship dbRel = null;
        // Note: source maybe null, e.g. an abstract entity.
        if (dbSrc != null && dbTarget != null) {
            // in case of inheritance EOF stores duplicates of all inherited
            // relationships, so we must skip this relationship in DB entity
            // if it is
            // already there...
            dbRel = dbSrc.getRelationship(relName);
            if (dbRel == null) {
                dbRel = new DbRelationship();
                dbRel.setSourceEntity(dbSrc);
                dbRel.setTargetEntityName(dbTarget);
                dbRel.setToMany(toMany);
                dbRel.setName(relName);
                dbRel.setToDependentPK(toDependentPK);
                dbSrc.addRelationship(dbRel);
                List joins = (List) relMap.get("joins");
                Iterator jIt = joins.iterator();
                while (jIt.hasNext()) {
                    Map joinMap = (Map) jIt.next();
                    DbJoin join = new DbJoin(dbRel);
                    // find source attribute dictionary and extract the
                    // column name
                    String sourceAttributeName = (String) joinMap.get("sourceAttribute");
                    join.setSourceName(columnName(attributes, sourceAttributeName));
                    String targetAttributeName = (String) joinMap.get("destinationAttribute");
                    join.setTargetName(columnName(targetAttributes, targetAttributeName));
                    dbRel.addJoin(join);
                }
            }
        }
        // only create obj relationship if it is a class property
        if (classProps.contains(relName)) {
            ObjRelationship rel = new ObjRelationship();
            rel.setName(relName);
            rel.setSourceEntity(objEntity);
            rel.setTargetEntityName(target);
            objEntity.addRelationship(rel);
            if (dbRel != null) {
                rel.addDbRelationship(dbRel);
            }
        }
    }
}
Also used : ObjEntity(org.apache.cayenne.map.ObjEntity) ObjRelationship(org.apache.cayenne.map.ObjRelationship) DbEntity(org.apache.cayenne.map.DbEntity) DbRelationship(org.apache.cayenne.map.DbRelationship) Iterator(java.util.Iterator) Collection(java.util.Collection) DbJoin(org.apache.cayenne.map.DbJoin) ArrayList(java.util.ArrayList) List(java.util.List) Map(java.util.Map) DataMap(org.apache.cayenne.map.DataMap)

Aggregations

DbJoin (org.apache.cayenne.map.DbJoin)60 DbRelationship (org.apache.cayenne.map.DbRelationship)42 DbEntity (org.apache.cayenne.map.DbEntity)25 DbAttribute (org.apache.cayenne.map.DbAttribute)23 ObjRelationship (org.apache.cayenne.map.ObjRelationship)16 ObjAttribute (org.apache.cayenne.map.ObjAttribute)12 ObjEntity (org.apache.cayenne.map.ObjEntity)11 CayenneRuntimeException (org.apache.cayenne.CayenneRuntimeException)9 ClassDescriptor (org.apache.cayenne.reflect.ClassDescriptor)9 ArrayList (java.util.ArrayList)8 AttributeProperty (org.apache.cayenne.reflect.AttributeProperty)7 PropertyVisitor (org.apache.cayenne.reflect.PropertyVisitor)7 ToManyProperty (org.apache.cayenne.reflect.ToManyProperty)7 ToOneProperty (org.apache.cayenne.reflect.ToOneProperty)7 HashMap (java.util.HashMap)6 Test (org.junit.Test)6 QuotingStrategy (org.apache.cayenne.dba.QuotingStrategy)5 EJBQLException (org.apache.cayenne.ejbql.EJBQLException)5 HashSet (java.util.HashSet)4 List (java.util.List)4