use of org.apache.cayenne.map.DbRelationship 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.DbRelationship in project cayenne by apache.
the class DataDomainQueryAction method interceptRelationshipQuery.
private boolean interceptRelationshipQuery() {
if (query instanceof RelationshipQuery) {
RelationshipQuery relationshipQuery = (RelationshipQuery) query;
if (relationshipQuery.isRefreshing()) {
return !DONE;
}
ObjRelationship relationship = relationshipQuery.getRelationship(domain.getEntityResolver());
// check if we can derive target PK from FK...
if (relationship.isSourceIndependentFromTargetChange()) {
return !DONE;
}
// we can assume that there is one and only one DbRelationship as
// we previously checked that "!isSourceIndependentFromTargetChange"
DbRelationship dbRelationship = relationship.getDbRelationships().get(0);
// FK pointing to a unique field that is a 'fake' PK (CAY-1755)...
// It is not sufficient to generate target ObjectId.
DbEntity targetEntity = dbRelationship.getTargetEntity();
if (dbRelationship.getJoins().size() < targetEntity.getPrimaryKeys().size()) {
return !DONE;
}
if (cache == null) {
return !DONE;
}
DataRow sourceRow = cache.getCachedSnapshot(relationshipQuery.getObjectId());
if (sourceRow == null) {
return !DONE;
}
ObjectId targetId = sourceRow.createTargetObjectId(relationship.getTargetEntityName(), dbRelationship);
// null id means that FK is null...
if (targetId == null) {
this.response = new GenericResponse(Collections.EMPTY_LIST);
return DONE;
}
// target id resolution (unlike source) should be polymorphic
DataRow targetRow = polymorphicRowFromCache(targetId);
if (targetRow != null) {
this.response = new GenericResponse(Collections.singletonList(targetRow));
return DONE;
}
// create a fault
if (context != null && relationship.isSourceDefiningTargetPrecenseAndType(domain.getEntityResolver())) {
// prevent passing partial snapshots to ObjectResolver per
// CAY-724.
// Create a hollow object right here and skip object conversion
// downstream
this.noObjectConversion = true;
Object object = context.findOrCreateObject(targetId);
this.response = new GenericResponse(Collections.singletonList(object));
return DONE;
}
}
return !DONE;
}
use of org.apache.cayenne.map.DbRelationship in project cayenne by apache.
the class DataNodeSyncQualifierDescriptor method reset.
void reset(DbEntityClassDescriptor descriptor) {
attributes = new ArrayList<>(3);
valueTransformers = new ArrayList<>(3);
usingOptimisticLocking = descriptor.getEntity().getLockType() == ObjEntity.LOCK_TYPE_OPTIMISTIC;
// master PK columns
if (descriptor.isMaster()) {
for (final DbAttribute attribute : descriptor.getDbEntity().getPrimaryKeys()) {
attributes.add(attribute);
valueTransformers.add(input -> {
ObjectId id = (ObjectId) input.getNodeId();
return id.getIdSnapshot().get(attribute.getName());
});
}
} else {
// TODO: andrus 12/23/2007 - only one step relationship is supported...
if (descriptor.getPathFromMaster().size() != 1) {
throw new CayenneRuntimeException("Only single step dependent relationships are currently supported. Actual path length: %d", descriptor.getPathFromMaster().size());
}
DbRelationship masterDependentDbRel = descriptor.getPathFromMaster().get(0);
if (masterDependentDbRel != null) {
for (final DbJoin dbAttrPair : masterDependentDbRel.getJoins()) {
DbAttribute dbAttribute = dbAttrPair.getTarget();
if (!attributes.contains(dbAttribute)) {
attributes.add(dbAttribute);
valueTransformers.add(input -> {
ObjectId id = (ObjectId) input.getNodeId();
return id.getIdSnapshot().get(dbAttrPair.getSourceName());
});
}
}
}
}
if (usingOptimisticLocking) {
for (final ObjAttribute attribute : descriptor.getEntity().getAttributes()) {
if (attribute.isUsedForLocking()) {
// only care about first step in a flattened attribute
DbAttribute dbAttribute = (DbAttribute) attribute.getDbPathIterator().next();
// only use qualifier if dbEntities match
if (dbAttribute.getEntity().equals(descriptor.getDbEntity()) && !attributes.contains(dbAttribute)) {
attributes.add(dbAttribute);
valueTransformers.add(input -> input.getSnapshotValue(attribute.getName()));
}
}
}
for (final ObjRelationship relationship : descriptor.getEntity().getRelationships()) {
if (relationship.isUsedForLocking()) {
// only care about the first DbRelationship
DbRelationship dbRelationship = relationship.getDbRelationships().get(0);
for (final DbJoin dbAttrPair : dbRelationship.getJoins()) {
DbAttribute dbAttribute = dbAttrPair.getSource();
// relationship transformers override attribute transformers for meaningful FK's...
// why meaningful FKs can go out of sync is another story (CAY-595)
int index = attributes.indexOf(dbAttribute);
if (index >= 0 && !dbAttribute.isForeignKey()) {
continue;
}
Function<ObjectDiff, Object> transformer = input -> {
ObjectId targetId = input.getArcSnapshotValue(relationship.getName());
return targetId != null ? targetId.getIdSnapshot().get(dbAttrPair.getTargetName()) : null;
};
if (index < 0) {
attributes.add(dbAttribute);
valueTransformers.add(transformer);
} else {
valueTransformers.set(index, transformer);
}
}
}
}
}
}
use of org.apache.cayenne.map.DbRelationship in project cayenne by apache.
the class FlattenedArcKey method lazyJoinSnapshot.
private Map<String, Object> lazyJoinSnapshot() {
List<DbRelationship> relList = relationship.getDbRelationships();
if (relList.size() != 2) {
throw new CayenneRuntimeException("Only single-step flattened relationships are supported in this operation: %s", relationship);
}
DbRelationship firstDbRel = relList.get(0);
DbRelationship secondDbRel = relList.get(1);
List<DbJoin> fromSourceJoins = firstDbRel.getJoins();
List<DbJoin> toTargetJoins = secondDbRel.getJoins();
Map<String, Object> snapshot = new HashMap<>(fromSourceJoins.size() + toTargetJoins.size(), 1);
for (DbJoin join : fromSourceJoins) {
Object value = new PropagatedValueFactory(id1.getSourceId(), join.getSourceName());
snapshot.put(join.getTargetName(), value);
}
for (DbJoin join : toTargetJoins) {
Object value = new PropagatedValueFactory(id2.getSourceId(), join.getTargetName());
snapshot.put(join.getSourceName(), value);
}
return snapshot;
}
use of org.apache.cayenne.map.DbRelationship in project cayenne by apache.
the class ObjectDiff method addPhantomFkDiff.
private void addPhantomFkDiff(ArcOperation arcDiff) {
String arcId = arcDiff.getArcId().toString();
DbEntity dbEntity = classDescriptor.getEntity().getDbEntity();
DbRelationship dbRelationship = (DbRelationship) dbEntity.getRelationship(arcId.substring(ASTDbPath.DB_PREFIX.length()));
if (dbRelationship.isToMany()) {
return;
}
if (currentArcSnapshot == null) {
currentArcSnapshot = new HashMap<>();
}
currentArcSnapshot.put(arcId, arcDiff.getTargetNodeId());
if (phantomFks == null) {
phantomFks = new HashMap<>();
}
ArcOperation oldOp = phantomFks.put(arcDiff, arcDiff);
// "delete" cancels "create" and vice versa...
if (oldOp != null && oldOp.isDelete() != arcDiff.isDelete()) {
phantomFks.remove(arcDiff);
if (otherDiffs != null) {
otherDiffs.remove(oldOp);
}
}
}
Aggregations