Re: Modified Fields

From: Bryan Lewis (brya..aine.rr.com)
Date: Mon Nov 01 2010 - 14:03:12 UTC

  • Next message: Bruno René Santos: "RE: Modified Fields"

    I've written code that logs changes to the database. I'm appending it below
    with some disclaimers. It's stable -- we've been using it in production for
    several years now -- but it might be more complicated than you need. I've
    boiled it down somewhat here... removed the frill of ignoring a set of
    specified entity and attribute names. Maybe the only parts you'll care
    about are in the first quarter, where it collects all the changed objects
    and looks at each one's snapshot.

    When you see ModelObject, think CayenneDataObject; ModelObject is my "wedge"
    super-class.

        /**
         * For each object in the list of modified, new or deleted objects,
         * write a ChangeHistory record if there was a real change in an
    attribute
         * or a to-one relationship. Called immediately before
    dc.commitChanges().
         */
       ..uppressWarnings("unchecked")
        private static void processRealChanges(DataContext dc)
        {
            // Collect all the changed objects together.
            Collection<ModelObject> objects = (Collection<ModelObject>)
    dc.newObjects();
            objects.addAll((List<ModelObject>) dc.modifiedObjects());
            objects.addAll((List<ModelObject>) dc.deletedObjects());

            for (ModelObject object : objects) {
                // Get the class name without the "model." prefix.
                ObjEntity entity = object.getObjEntity();
                String entityName = object.getEntityName();
                // If it's new, write a single history record with
                // object.toStringCompact() as the change description.
                if (object.isNew()) {
                    String change = object.toStringCompact();
                    writeHistory(dc, object, change,
    RDChangeType.changeTypeInsert(dc));
                    continue;
                }

                // If it's deleted, write a single history record.
                if (object.isDeleted()) {
                    String change = object.toStringCompact();
                    writeHistory(dc, object, change,
    RDChangeType.changeTypeDelete(dc));
                    continue;
                }

                // The rest is for modified objects.
                Map snapshot =
    dc.getObjectStore().getSnapshot(object.getObjectId());
                List<String> changes = null;
                changes = CollFactory.newList();

                // First the attributes.
                Collection<ObjAttribute> attributes = entity.getAttributes();
                for (ObjAttribute attribute : attributes) {
                    String name = attribute.getName();
                    Object newValue = object.readProperty(name);

                    // The snapshot uses database attribute names.
                    String dbName = attribute.getDbAttributePath();
                    Object oldValue = snapshot.get(dbName);
                    if (valueChanged(oldValue, newValue)) {
                        String desc = null;
                        desc = entityName + "." + name;
                        log.debug("# Real change in attribute " + desc);
                        changes.add(changeDescription(name, oldValue,
    newValue));
                    }
                }

                // Then the to-one relationships.
                String pkName = null;
                DbEntity dbEntity = entity.getDbEntity();
                Collection<DbAttribute> pks = dbEntity.getPrimaryKeys();
                if (pks.size() == 1) {
                    DbAttribute pk = pks.iterator().next();
                    pkName = pk.getName();
                }

                for (ObjRelationship relationship : entity.getRelationships()) {
                    if (relationship.isToMany()) {
                        continue;
                    }

                    // Guided by code from cayenne's access.DataRowUtils...
    determine
                    // whether there was a change by looking at the target
    objectId.
                    String name = relationship.getName();
                    Object targetObject = object.readPropertyDirectly(name);

                    // If it's a fault it wasn't modified.
                    if (targetObject instanceof Fault) {
                        continue;
                    }

                    ModelObject toOneTarget = (ModelObject) targetObject;

                    // Compare the db values.
                    DbRelationship dbRelationship =
    relationship.getDbRelationships().get(0);
                    DbAttribute dbAttribute =
    dbRelationship.getSourceAttributes().iterator().next();
                    String sourceAttributeName = dbAttribute.getName();
                    Object oldValue = snapshot.get(sourceAttributeName);
                    dbAttribute =
    dbRelationship.getTargetAttributes().iterator().next();
                    String targetAttributeName = dbAttribute.getName();
                    Object newValue = null;
                    if (toOneTarget != null && (toOneTarget.isPersistent() ||
    toOneTarget.isHollow())) {
                        newValue =
    toOneTarget.getSnapshotDbProperty(targetAttributeName);
                    }

                    // Be careful if the newValue is null and the relationship
    is based on
                    // the source object's PK which can't be null. For example,
    Company.rating
                    // is based on the company's PK but not all companies have a
    rating.
                    boolean ignoreNewNullValue = (newValue == null &&
    sourceAttributeName.equals(pkName));
                    if (!ignoreNewNullValue && valueChanged(oldValue, newValue))
    {
                        changes.add(changeDescription(name, oldValue,
    newValue));
                    }
                }

                if (changes.size() > 0) {
                    Collections.sort(changes);
                    String changeDescription =
    CString.componentsJoinedByString(changes, "\n");
                    writeHistory(dc, object, changeDescription,
    RDChangeType.changeTypeUpdate(dc));
                }
            }
        }

    On Mon, Nov 1, 2010 at 9:27 AM, Mike Kienenberger <mkienen..mail.com>wrote:

    > It's been a while, so I don't remember if it is still relevant for 3.x, but
    >
    > https://issues.apache.org/jira/browse/CAY-414
    >
    > contains code for audit logging under Cayenne 2.0.
    >
    >
    > On Sat, Oct 30, 2010 at 10:49 AM, Bruno René Santos <brunoren..olos.pt>
    > wrote:
    > > I only found the hasChanges()... there is, on the other hand, a
    > > getSnapshot(ObjectId) function. Could I use it to compare to my
    > > CayenneDataObject, field by field, in order to check which ones were
    > changed?
    > > This is because I need to create log entries where for each update I only
    > > specify the key-value pair for the modified fields.
    > >
    > > Thanx
    > > Bruno
    > >
    > > -----Mensagem original-----
    > > De: Michael Gentry [mailto:mgentr..asslight.net]
    > > Enviada: sexta-feira, 29 de Outubro de 2010 16:59
    > > Para: use..ayenne.apache.org
    > > Assunto: Re: Modified Fields
    > >
    > > Hi Bruno,
    > >
    > > Try dataContext.getObjectStore().getChanges().
    > >
    > > mrg
    > >
    > >
    > > On Fri, Oct 29, 2010 at 11:43 AM, Bruno René Santos <brunoren..olos.pt>
    > wrote:
    > >> Hello all,
    > >>
    > >> Anyone knows how can I know which fields on a CayenneDataObject were
    > modified
    > >> since the last read?
    > >>
    > >> Thanx
    > >> Bruno Santos
    > >>
    > >>
    > >
    > >
    >



    This archive was generated by hypermail 2.0.0 : Mon Nov 01 2010 - 14:03:44 UTC