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;
}
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);
}
}
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);
}
}
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);
}
}
}
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);
}
}
}
}
Aggregations