use of org.apache.cayenne.map.DbRelationship in project cayenne by apache.
the class DbRelationshipValidator method checkForDuplicates.
/**
* Per CAY-1813, make sure two (or more) DbRelationships do not map to the
* same database path.
*/
private void checkForDuplicates(DbRelationship relationship, ValidationResult validationResult) {
if (relationship != null && relationship.getName() != null && relationship.getTargetEntityName() != null) {
String dbRelationshipPath = relationship.getTargetEntityName() + "." + getJoins(relationship);
DbEntity entity = relationship.getSourceEntity();
for (DbRelationship comparisonRelationship : entity.getRelationships()) {
if (relationship != comparisonRelationship) {
String comparisonDbRelationshipPath = comparisonRelationship.getTargetEntityName() + "." + getJoins(comparisonRelationship);
if (dbRelationshipPath.equals(comparisonDbRelationshipPath)) {
addFailure(validationResult, relationship, "DbEntity '%s' contains a duplicate DbRelationship mapping ('%s' -> '%s')", entity.getName(), relationship.getName(), dbRelationshipPath);
// Duplicate found, stop.
return;
}
}
}
}
}
use of org.apache.cayenne.map.DbRelationship in project cayenne by apache.
the class ObjRelationshipValidator method validate.
void validate(ObjRelationship relationship, ValidationResult validationResult) {
if (Util.isEmptyString(relationship.getName())) {
addFailure(validationResult, relationship, "Unnamed ObjRelationship");
} else if (relationship.getSourceEntity().getAttribute(relationship.getName()) != null) {
// check if there are attributes having the same name
addFailure(validationResult, relationship, "ObjRelationship '%s' has the same name as one of ObjAttributes", toString(relationship));
} else {
NameValidationHelper helper = NameValidationHelper.getInstance();
String invalidChars = helper.invalidCharsInObjPathComponent(relationship.getName());
if (invalidChars != null) {
addFailure(validationResult, relationship, "ObjRelationship name '%s' contains invalid characters: %s", toString(relationship), invalidChars);
} else if (helper.invalidDataObjectProperty(relationship.getName())) {
addFailure(validationResult, relationship, "ObjRelationship name '%s' is a reserved word", toString(relationship));
}
}
if (relationship.getTargetEntity() == null) {
addFailure(validationResult, relationship, "ObjRelationship '%s' has no target entity", toString(relationship));
} else {
// check for missing DbRelationship mappings
List<DbRelationship> dbRels = relationship.getDbRelationships();
if (dbRels.isEmpty()) {
addFailure(validationResult, relationship, "ObjRelationship '%s' has no DbRelationship mapping", toString(relationship));
} else {
DbEntity expectedSrc = relationship.getSourceEntity().getDbEntity();
DbEntity expectedTarget = relationship.getTargetEntity().getDbEntity();
if ((dbRels.get(0)).getSourceEntity() != expectedSrc || (dbRels.get(dbRels.size() - 1)).getTargetEntity() != expectedTarget) {
addFailure(validationResult, relationship, "ObjRelationship '%s' has incomplete DbRelationship mapping", toString(relationship));
}
}
}
// foreign key attributes are mandatory.
if (relationship.isToMany() && !relationship.isFlattened() && (relationship.getDeleteRule() == DeleteRule.NULLIFY)) {
ObjRelationship inverse = relationship.getReverseRelationship();
if (inverse != null) {
DbRelationship firstRel = inverse.getDbRelationships().get(0);
Iterator<DbJoin> attributePairIterator = firstRel.getJoins().iterator();
// by default, the relation will be check for mandatory.
boolean check = true;
while (attributePairIterator.hasNext()) {
DbJoin pair = attributePairIterator.next();
if (!pair.getSource().isMandatory()) {
// a field of the fk can be nullable, cancel the check.
check = false;
break;
}
}
if (check) {
addFailure(validationResult, relationship, "ObjRelationship '%s' has a Nullify delete rule and a mandatory reverse relationship", toString(relationship));
}
}
}
// check for relationships with same source and target entities
ObjEntity entity = relationship.getSourceEntity();
for (ObjRelationship rel : entity.getRelationships()) {
if (relationship.getDbRelationshipPath() != null && relationship.getDbRelationshipPath().equals(rel.getDbRelationshipPath())) {
if (relationship != rel && relationship.getTargetEntity() == rel.getTargetEntity() && relationship.getSourceEntity() == rel.getSourceEntity()) {
addFailure(validationResult, relationship, "ObjectRelationship '%s' duplicates relationship '%s'", toString(relationship), toString(rel));
}
}
}
// check for invalid relationships in inherited entities
if (relationship.getReverseRelationship() != null) {
ObjRelationship revRel = relationship.getReverseRelationship();
if (relationship.getSourceEntity() != revRel.getTargetEntity() || relationship.getTargetEntity() != revRel.getSourceEntity()) {
addFailure(validationResult, revRel, "Usage of super entity's relationships '%s' as reversed relationships for sub entity is discouraged", toString(revRel));
}
}
checkForDuplicates(relationship, validationResult);
}
use of org.apache.cayenne.map.DbRelationship in project cayenne by apache.
the class QualifierTranslator method appendObjectMatch.
protected void appendObjectMatch() throws IOException {
if (!matchingObject || objectMatchTranslator == null) {
throw new IllegalStateException("An invalid attempt to append object match.");
}
// turn off special handling, so that all the methods behave as a
// superclass's
// impl.
matchingObject = false;
boolean first = true;
DbRelationship relationship = objectMatchTranslator.getRelationship();
if (!relationship.isToMany() && !relationship.isToPK()) {
queryAssembler.dbRelationshipAdded(relationship, JoinType.INNER, objectMatchTranslator.getJoinSplitAlias());
}
Iterator<String> it = objectMatchTranslator.keys();
while (it.hasNext()) {
if (first) {
first = false;
} else {
out.append(" AND ");
}
String key = it.next();
DbAttribute attr = objectMatchTranslator.getAttribute(key);
Object val = objectMatchTranslator.getValue(key);
processColumn(attr);
out.append(objectMatchTranslator.getOperation());
appendLiteral(val, attr, objectMatchTranslator.getExpression());
}
objectMatchTranslator.reset();
}
use of org.apache.cayenne.map.DbRelationship in project cayenne by apache.
the class AshwoodEntitySorter method sortObjectsForEntity.
@Override
public void sortObjectsForEntity(ObjEntity objEntity, List<?> objects, boolean deleteOrder) {
indexSorter();
List<Persistent> persistent = (List<Persistent>) objects;
DbEntity dbEntity = objEntity.getDbEntity();
// if no sorting is required
if (!isReflexive(dbEntity)) {
return;
}
int size = persistent.size();
if (size == 0) {
return;
}
EntityResolver resolver = persistent.get(0).getObjectContext().getEntityResolver();
ClassDescriptor descriptor = resolver.getClassDescriptor(objEntity.getName());
List<DbRelationship> reflexiveRels = reflexiveDbEntities.get(dbEntity);
String[] reflexiveRelNames = new String[reflexiveRels.size()];
for (int i = 0; i < reflexiveRelNames.length; i++) {
DbRelationship dbRel = reflexiveRels.get(i);
ObjRelationship objRel = (dbRel != null ? objEntity.getRelationshipForDbRelationship(dbRel) : null);
reflexiveRelNames[i] = (objRel != null ? objRel.getName() : null);
}
List<Persistent> sorted = new ArrayList<>(size);
Digraph<Persistent, Boolean> objectDependencyGraph = new MapDigraph<>();
Object[] masters = new Object[reflexiveRelNames.length];
for (int i = 0; i < size; i++) {
Persistent current = (Persistent) objects.get(i);
objectDependencyGraph.addVertex(current);
int actualMasterCount = 0;
for (int k = 0; k < reflexiveRelNames.length; k++) {
String reflexiveRelName = reflexiveRelNames[k];
if (reflexiveRelName == null) {
continue;
}
masters[k] = descriptor.getProperty(reflexiveRelName).readProperty(current);
if (masters[k] == null) {
masters[k] = findReflexiveMaster(current, objEntity.getRelationship(reflexiveRelName), current.getObjectId().getEntityName());
}
if (masters[k] != null) {
actualMasterCount++;
}
}
int mastersFound = 0;
for (int j = 0; j < size && mastersFound < actualMasterCount; j++) {
if (i == j) {
continue;
}
Persistent masterCandidate = persistent.get(j);
for (Object master : masters) {
if (masterCandidate == master) {
objectDependencyGraph.putArc(masterCandidate, current, Boolean.TRUE);
mastersFound++;
}
}
}
}
IndegreeTopologicalSort<Persistent> sorter = new IndegreeTopologicalSort<>(objectDependencyGraph);
while (sorter.hasNext()) {
Persistent o = sorter.next();
if (o == null) {
throw new CayenneRuntimeException("Sorting objects for %s failed. Cycles found.", objEntity.getClassName());
}
sorted.add(o);
}
// since API requires sorting within the same array,
// simply replace all objects with objects in the right order...
// may come up with something cleaner later
persistent.clear();
persistent.addAll(sorted);
if (deleteOrder) {
Collections.reverse(persistent);
}
}
use of org.apache.cayenne.map.DbRelationship in project cayenne by apache.
the class AshwoodEntitySorter method findReflexiveMaster.
protected Object findReflexiveMaster(Persistent object, ObjRelationship toOneRel, String targetEntityName) {
DbRelationship finalRel = toOneRel.getDbRelationships().get(0);
ObjectContext context = object.getObjectContext();
// the method
if (object.getObjectId().isTemporary()) {
return null;
}
ObjectIdQuery query = new ObjectIdQuery(object.getObjectId(), true, ObjectIdQuery.CACHE);
QueryResponse response = context.getChannel().onQuery(null, query);
List<?> result = response.firstList();
if (result == null || result.size() == 0) {
return null;
}
DataRow snapshot = (DataRow) result.get(0);
ObjectId id = snapshot.createTargetObjectId(targetEntityName, finalRel);
// hollow objects
return (id != null) ? context.getGraphManager().getNode(id) : null;
}
Aggregations