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