@Override public void nodeRemoved(Object nodeId) { ObjectId id = (ObjectId) nodeId; final MutableObjectChange objectChangeSet = changeSet.getOrCreate(id, ObjectChangeType.DELETE); // TODO: rewrite with SelectById query after Cayenne upgrade ObjectIdQuery query = new ObjectIdQuery(id, true, ObjectIdQuery.CACHE); QueryResponse result = channel.onQuery(null, query); @SuppressWarnings("unchecked") List<DataRow> rows = result.firstList(); if (rows.isEmpty()) { LOGGER.warn( "No DB snapshot for object to be deleted, no changes will be recorded. ID: " + id); return; } final DataRow row = rows.get(0); ClassDescriptor descriptor = channel.getEntityResolver().getClassDescriptor(id.getEntityName()); final PostCommitEntity entity = entityFactory.getEntity(id); descriptor.visitProperties( new PropertyVisitor() { @Override public boolean visitAttribute(AttributeProperty property) { if (!entity.isIncluded(property.getName())) { return true; } Object value; if (entity.isConfidential(property.getName())) { value = Confidential.getInstance(); } else { String key = property.getAttribute().getDbAttributeName(); value = row.get(key); } if (value != null) { objectChangeSet.attributeChanged(property.getName(), value, null); } return true; } @Override public boolean visitToOne(ToOneProperty property) { // TODO record FK changes? return true; } @Override public boolean visitToMany(ToManyProperty property) { return true; } }); }
/** * Registers a transient object with the context, recursively registering all transient persistent * objects attached to this object via relationships. * * <p><i>Note that since 3.0 this method takes Object as an argument instead of a {@link * DataObject}.</i> * * @param object new object that needs to be made persistent. */ @Override public void registerNewObject(Object object) { if (object == null) { throw new NullPointerException("Can't register null object."); } ObjEntity entity = getEntityResolver().getObjEntity((Persistent) object); if (entity == null) { throw new IllegalArgumentException( "Can't find ObjEntity for Persistent class: " + object.getClass().getName() + ", class is likely not mapped."); } final Persistent persistent = (Persistent) object; // sanity check - maybe already registered if (persistent.getObjectId() != null) { if (persistent.getObjectContext() == this) { // already registered, just ignore return; } else if (persistent.getObjectContext() != null) { throw new IllegalStateException( "Persistent is already registered with another DataContext. " + "Try using 'localObjects()' instead."); } } else { persistent.setObjectId(new ObjectId(entity.getName())); } ClassDescriptor descriptor = getEntityResolver().getClassDescriptor(entity.getName()); if (descriptor == null) { throw new IllegalArgumentException("Invalid entity name: " + entity.getName()); } injectInitialValue(object); // now we need to find all arc changes, inject missing value holders and // pull in // all transient connected objects descriptor.visitProperties( new PropertyVisitor() { public boolean visitToMany(ToManyProperty property) { property.injectValueHolder(persistent); if (!property.isFault(persistent)) { Object value = property.readProperty(persistent); Collection<Map.Entry> collection = (value instanceof Map) ? ((Map) value).entrySet() : (Collection) value; Iterator<Map.Entry> it = collection.iterator(); while (it.hasNext()) { Object target = it.next(); if (target instanceof Persistent) { Persistent targetDO = (Persistent) target; // make sure it is registered registerNewObject(targetDO); getObjectStore() .arcCreated( persistent.getObjectId(), targetDO.getObjectId(), property.getName()); } } } return true; } public boolean visitToOne(ToOneProperty property) { Object target = property.readPropertyDirectly(persistent); if (target instanceof Persistent) { Persistent targetDO = (Persistent) target; // make sure it is registered registerNewObject(targetDO); getObjectStore() .arcCreated(persistent.getObjectId(), targetDO.getObjectId(), property.getName()); } return true; } public boolean visitAttribute(AttributeProperty property) { return true; } }); }
/** * Returns a DataRow reflecting current, possibly uncommitted, object state. * * <p><strong>Warning:</strong> This method will return a partial snapshot if an object or one of * its related objects that propagate their keys to this object have temporary ids. DO NOT USE * this method if you expect a DataRow to represent a complete object state. * * @since 1.1 */ public DataRow currentSnapshot(final Persistent object) { // for a HOLLOW object return snapshot from cache if (object.getPersistenceState() == PersistenceState.HOLLOW && object.getObjectContext() != null) { return getObjectStore().getSnapshot(object.getObjectId()); } ObjEntity entity = getEntityResolver().getObjEntity(object); final ClassDescriptor descriptor = getEntityResolver().getClassDescriptor(entity.getName()); final DataRow snapshot = new DataRow(10); snapshot.setEntityName(entity.getName()); descriptor.visitProperties( new PropertyVisitor() { public boolean visitAttribute(AttributeProperty property) { ObjAttribute objAttr = property.getAttribute(); // processing compound attributes correctly snapshot.put(objAttr.getDbAttributePath(), property.readPropertyDirectly(object)); return true; } public boolean visitToMany(ToManyProperty property) { // do nothing return true; } public boolean visitToOne(ToOneProperty property) { ObjRelationship rel = property.getRelationship(); // if target doesn't propagates its key value, skip it if (rel.isSourceIndependentFromTargetChange()) { return true; } Object targetObject = property.readPropertyDirectly(object); if (targetObject == null) { return true; } // if target is Fault, get id attributes from stored snapshot // to avoid unneeded fault triggering if (targetObject instanceof Fault) { DataRow storedSnapshot = getObjectStore().getSnapshot(object.getObjectId()); if (storedSnapshot == null) { throw new CayenneRuntimeException( "No matching objects found for ObjectId " + object.getObjectId() + ". Object may have been deleted externally."); } DbRelationship dbRel = rel.getDbRelationships().get(0); for (DbJoin join : dbRel.getJoins()) { String key = join.getSourceName(); snapshot.put(key, storedSnapshot.get(key)); } return true; } // target is resolved and we have an FK->PK to it, // so extract it from target... Persistent target = (Persistent) targetObject; Map<String, Object> idParts = target.getObjectId().getIdSnapshot(); // this may happen in uncommitted objects - see the warning in // the JavaDoc // of // this method. if (idParts.isEmpty()) { return true; } DbRelationship dbRel = rel.getDbRelationships().get(0); Map<String, Object> fk = dbRel.srcFkSnapshotWithTargetSnapshot(idParts); snapshot.putAll(fk); return true; } }); // process object id map // we should ignore any object id values if a corresponding attribute // is a part of relationship "toMasterPK", since those values have been // set above when db relationships where processed. Map<String, Object> thisIdParts = object.getObjectId().getIdSnapshot(); if (thisIdParts != null) { // put only those that do not exist in the map for (Map.Entry<String, Object> entry : thisIdParts.entrySet()) { String nextKey = entry.getKey(); if (!snapshot.containsKey(nextKey)) { snapshot.put(nextKey, entry.getValue()); } } } return snapshot; }
@Override public void prepareForAccess(Persistent object, String property, boolean lazyFaulting) { if (object.getPersistenceState() == PersistenceState.HOLLOW) { ObjectId oid = object.getObjectId(); List<?> objects = performQuery(new ObjectIdQuery(oid, false, ObjectIdQuery.CACHE)); if (objects.size() == 0) { throw new FaultFailureException( "Error resolving fault, no matching row exists in the database for ObjectId: " + oid); } else if (objects.size() > 1) { throw new FaultFailureException( "Error resolving fault, more than one row exists in the database for ObjectId: " + oid); } // 5/28/2013 - Commented out this block to allow for modifying // objects in the postLoad callback // sanity check... // if (object.getPersistenceState() != PersistenceState.COMMITTED) { // // String state = // PersistenceState.persistenceStateName(object.getPersistenceState()); // // // TODO: andrus 4/13/2006, modified and deleted states are // // possible due to // // a race condition, should we handle them here? // throw new // FaultFailureException("Error resolving fault for ObjectId: " + // oid + " and state (" + state // + // "). Possible cause - matching row is missing from the database."); // } } // resolve relationship fault if (lazyFaulting && property != null) { ClassDescriptor classDescriptor = getEntityResolver().getClassDescriptor(object.getObjectId().getEntityName()); PropertyDescriptor propertyDescriptor = classDescriptor.getProperty(property); // If we don't have a property descriptor, there's not much we can // do. // Let the caller know that the specified property could not be // found and list // all of the properties that could be so the caller knows what can // be used. if (propertyDescriptor == null) { final StringBuilder errorMessage = new StringBuilder(); errorMessage.append( String.format( "Property '%s' is not declared for entity '%s'.", property, object.getObjectId().getEntityName())); errorMessage.append(" Declared properties are: "); // Grab each of the declared properties. final List<String> properties = new ArrayList<String>(); classDescriptor.visitProperties( new PropertyVisitor() { @Override public boolean visitAttribute(final AttributeProperty property) { properties.add(property.getName()); return true; } @Override public boolean visitToOne(final ToOneProperty property) { properties.add(property.getName()); return true; } @Override public boolean visitToMany(final ToManyProperty property) { properties.add(property.getName()); return true; } }); // Now add the declared property names to the error message. boolean first = true; for (String declaredProperty : properties) { if (first) { errorMessage.append(String.format("'%s'", declaredProperty)); first = false; } else { errorMessage.append(String.format(", '%s'", declaredProperty)); } } errorMessage.append("."); throw new CayenneRuntimeException(errorMessage.toString()); } // this should trigger fault resolving propertyDescriptor.readProperty(object); } }