cayenne-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Mike Kienenberger <mkien...@gmail.com>
Subject Re: Obtain primary key for DataObject before commitChanges
Date Mon, 10 Aug 2015 12:53:34 GMT
I set up auditing using a different approach in one project many years
ago back in Cayenne 1.1, and I've continued using it up to this point
in 3.x.   I generated special setter, addTo, and removeFrom methods as
well as a create method which created the logger object at that point.

To get the primary key, I set up one-way object relationships between
the primary key of the logged object and the foreign key storage field
in the audit log, one for each object entity.   When the commit
happened, the relationships automatically populated the audit log
fields.   You probably can do the same thing.   There's probably a
better way to set up the object relationships than manually defining
them in your model these days.

In a different Cayenne 1.2 project, I used something similar to your
pre-persist hook, although we didn't have prePersist yet.  For this
one, I had two foreign record key fields, one used for a single column
primary key and one used for compound primary keys.   I'm not sure if
the code will still work outside of 1.2 since this project was never
upgraded, but here it is in case you want to try this approach and
adapt it to a more recent version of Cayenne.
setForeignKeyRepresentation(Map pkAttributes, DbEntity dbEntity, Map
auditRecordMap) and the code that calls it after setting pkAttributes
would likely be what you want to reference for fetching the primary
key dynamically.


import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.objectstyle.cayenne.CayenneRuntimeException;
import org.objectstyle.cayenne.ObjectId;
import org.objectstyle.cayenne.access.DataContext;
import org.objectstyle.cayenne.access.DataDomainFlushObserver;
import org.objectstyle.cayenne.access.DataNode;
import org.objectstyle.cayenne.access.DefaultDataContextDelegate;
import org.objectstyle.cayenne.dba.PkGenerator;
import org.objectstyle.cayenne.map.DataMap;
import org.objectstyle.cayenne.map.DbAttribute;
import org.objectstyle.cayenne.map.DbEntity;
import org.objectstyle.cayenne.map.ObjEntity;
import org.objectstyle.cayenne.query.BatchQuery;
import org.objectstyle.cayenne.query.DeleteBatchQuery;
import org.objectstyle.cayenne.query.InsertBatchQuery;
import org.objectstyle.cayenne.query.UpdateBatchQuery;

public class AuditLoggingDataContextDelegate extends
DefaultDataContextDelegate implements Serializable
{
    public static final String MOD_TYPE_INSERT = "I";
    public static final String MOD_TYPE_UPDATE = "U";
    public static final String MOD_TYPE_DELETE = "D";

    public void finishedRunQueries(DataContext dataContext, List queryList) {
        super.finishedRunQueries(dataContext, queryList);

        Date modificationDate = new Date();

        List auditRecordMapList = new ArrayList();

        Iterator queryIterator = queryList.iterator();
        while (queryIterator.hasNext()) {
            BatchQuery batchQuery = (BatchQuery) queryIterator.next();

            if (batchQuery instanceof InsertBatchQuery)
            {
                InsertBatchQuery insertBatchQuery =
(InsertBatchQuery)batchQuery;
                insertBatchQuery.reset();

                List dbAttributes = insertBatchQuery.getDbAttributes();
                while(insertBatchQuery.next()) {
                    for(int i = 0; i < dbAttributes.size(); i++) {
                        Map auditRecordMap = new HashMap();

                        DbAttribute dbAttribute = (DbAttribute)
dbAttributes.get(i);
                       Object value = insertBatchQuery.getValue(i);

                       DbEntity dbEntity = (DbEntity)dbAttribute.getEntity();
                       auditRecordMap.put("MOD_TIME", modificationDate);
                       auditRecordMap.put("SCHEMA_NAME", dbEntity.getSchema());
                       auditRecordMap.put("TBL_NAME", dbEntity.getName());
                       auditRecordMap.put("COL_NAME", dbAttribute.getName());
                       auditRecordMap.put("MOD_TYPE", MOD_TYPE_INSERT);
                       if (null != value)
                       {
                           auditRecordMap.put("NEW_VALUE", value.toString());
                       }

                       Map pkAttributes;
                       ObjectId objectId = insertBatchQuery.getObjectId();
                       if (null != objectId)
                       {
                           pkAttributes = objectId.getIdSnapshot();
                       }
                       else
                       {
                           pkAttributes =
insertBatchQuery.getCurrentObjectSnapshot();
                       }
                       setForeignKeyRepresentation(pkAttributes,
dbEntity, auditRecordMap);

                        auditRecordMapList.add(auditRecordMap);
                    }
                }
            }
            else if (batchQuery instanceof DeleteBatchQuery)
            {
                DeleteBatchQuery deleteBatchQuery =
(DeleteBatchQuery)batchQuery;
                deleteBatchQuery.reset();

                List dbAttributes = deleteBatchQuery.getDbAttributes();
                while(deleteBatchQuery.next()) {
                    for(int i = 0; i < dbAttributes.size(); i++) {
                        Map auditRecordMap = new HashMap();

                        DbAttribute dbAttribute = (DbAttribute)
dbAttributes.get(i);

                        DbEntity dbEntity = (DbEntity)dbAttribute.getEntity();
                        auditRecordMap.put("MOD_TIME", modificationDate);
                        auditRecordMap.put("SCHEMA_NAME", dbEntity.getSchema());
                        auditRecordMap.put("TBL_NAME", dbEntity.getName());
                        auditRecordMap.put("COL_NAME", dbAttribute.getName());
                        auditRecordMap.put("MOD_TYPE", MOD_TYPE_DELETE);


setForeignKeyRepresentation(deleteBatchQuery.getCurrentQualifier(),
dbEntity, auditRecordMap);

                        auditRecordMapList.add(auditRecordMap);
                    }
                }
            }
            else if (batchQuery instanceof UpdateBatchQuery)
            {
                UpdateBatchQuery updateBatchQuery =
(UpdateBatchQuery)batchQuery;
                updateBatchQuery.reset();

                List dbAttributeList = updateBatchQuery.getUpdatedAttributes();
                while(updateBatchQuery.next()) {
                    for(int i = 0; i < dbAttributeList.size(); i++) {
                        Map auditRecordMap = new HashMap();

                        DbAttribute dbAttribute = (DbAttribute)
dbAttributeList.get(i);
                       Object newValue = updateBatchQuery.getValue(i);

                       Object oldValue = updateBatchQuery.getOldValue(i);

                       DbEntity dbEntity = (DbEntity)dbAttribute.getEntity();
                       auditRecordMap.put("MOD_TIME", modificationDate);
                       auditRecordMap.put("SCHEMA_NAME", dbEntity.getSchema());
                       auditRecordMap.put("TBL_NAME", dbEntity.getName());
                       auditRecordMap.put("COL_NAME", dbAttribute.getName());
                       auditRecordMap.put("MOD_TYPE", MOD_TYPE_UPDATE);
                       if (null != oldValue)
                       {
                           auditRecordMap.put("OLD_VALUE", oldValue.toString());
                       }
                       if (null != newValue)
                       {
                           auditRecordMap.put("NEW_VALUE", newValue.toString());
                       }


setForeignKeyRepresentation(batchQuery.getObjectId().getIdSnapshot(),
dbEntity, auditRecordMap);

                        auditRecordMapList.add(auditRecordMap);
                    }
                }
            }
        }

        processAuditRecordMapList(dataContext, auditRecordMapList);
    }

    protected void processAuditRecordMapList(DataContext dataContext,
List auditRecordMapList)
    {
        SecIndividual secIndividual =
(SecIndividual)dataContext.getUserProperty("secIndividual");
        SecSystem secSystem =
(SecSystem)dataContext.getUserProperty("secSystem");

        // Sort into ChangeLog records
        Map changeLogMap = new HashMap();
        Iterator auditRecordMapIterator = auditRecordMapList.iterator();
        while (auditRecordMapIterator.hasNext()) {
            Map auditRecordMap = (Map) auditRecordMapIterator.next();

            auditRecordMap.put("SYSTEM_ID", secSystem.getPrimaryKey());
            auditRecordMap.put("REAL_USER_ID", secIndividual.getPrimaryKey());
            auditRecordMap.put("EFFECTIVE_USER_ID",
secIndividual.getPrimaryKey());

            String tableName = (String)auditRecordMap.get("TBL_NAME");
            DbEntity dbEntity =
dataContext.getEntityResolver().getDbEntity(tableName);
            DataMap dataMap = dbEntity.getDataMap();
            String changeLogObjEntityName = "ChangeLog" + dataMap.getName();
            ObjEntity changeLogObjEntity =
dataMap.getObjEntity(changeLogObjEntityName);
            DbEntity changeLogDbEntity = changeLogObjEntity.getDbEntity();

            List changeLogList = (List)changeLogMap.get(changeLogDbEntity);
            if (null == changeLogList)
            {
                changeLogList = new ArrayList();
                changeLogMap.put(changeLogDbEntity, changeLogList);
            }

            changeLogList.add(auditRecordMap);
        }

        Iterator changeLogMapEntryIterator = changeLogMap.entrySet().iterator();
        while (changeLogMapEntryIterator.hasNext()) {
            Map.Entry changeLogEntry = (Map.Entry)
changeLogMapEntryIterator.next();
            DbEntity changeLogDbEntity = (DbEntity)changeLogEntry.getKey();
            List changeLogList = (List)changeLogEntry.getValue();

            InsertBatchQuery batch = new
InsertBatchQuery(changeLogDbEntity, changeLogList.size());

            DataNode node =
dataContext.getParentDataDomain().lookupDataNode(changeLogDbEntity.getDataMap());
            PkGenerator pkGenerator = node.getAdapter().getPkGenerator();
            List dbAttributeList = changeLogDbEntity.getPrimaryKey();
            if (1 != dbAttributeList.size())
            {
                throw new CayenneRuntimeException("Compound primary key");
            }
            DbAttribute keyAttribute = (DbAttribute)dbAttributeList.get(0);
            String key = keyAttribute.getName();
            Iterator changeLogIterator = changeLogList.iterator();
            while (changeLogIterator.hasNext()) {
                Map auditRecordMap = (Map) changeLogIterator.next();
                ObjectId id =
createObjectIdForAuditLog(changeLogDbEntity, node, pkGenerator, key);
                auditRecordMap.put(key, id.getIdSnapshot().get(key));
                batch.add(auditRecordMap, id);
            }

            DataDomainFlushObserver observer = new DataDomainFlushObserver();
            node.performQueries(Collections.singletonList(batch), observer);
        }

    }

    private ObjectId createObjectIdForAuditLog(DbEntity
changeLogDbEntity, DataNode node, PkGenerator pkGenerator, String key)
throws CayenneRuntimeException {
        Object pkValue;
        try {
            pkValue = pkGenerator.generatePkForDbEntity(node,
changeLogDbEntity);
        } catch (Exception e) {
            throw new CayenneRuntimeException("Error generating audit
log primary keys", e);
        }
        ObjectId id = new ObjectId(changeLogDbEntity.getName(), key, pkValue);
        return id;
    }

    private void setForeignKeyRepresentation(Map pkAttributes,
DbEntity dbEntity, Map auditRecordMap) {
        Integer primaryKeyOfRecord = null;
        String primaryKeysString = null;

        // References to the record that was changed (FK_C is for
compound keys, FK is for a single integer key).

        if (1 == pkAttributes.size())
        {
            Iterator pkIterator = pkAttributes.keySet().iterator();
            String primaryKeyName = (String) pkIterator.next();
            Object pkObject = pkAttributes.get(primaryKeyName);
            if (pkObject instanceof Integer)
            {
                primaryKeyOfRecord = (Integer)pkObject;
            }
        }

        if (null == primaryKeyOfRecord)
        {
             Iterator pkIterator = pkAttributes.keySet().iterator();
            while (pkIterator.hasNext())
            {
                String primaryKeyName = (String) pkIterator.next();
                Object primaryKeyValue = pkAttributes.get(primaryKeyName);

                if (null == primaryKeysString)
                {
                    primaryKeysString = primaryKeyName + "=" + primaryKeyValue;
                }
                else
                {
                    primaryKeysString = primaryKeysString + "," +
primaryKeyName + "=" + primaryKeyValue;
                }
            }
        }

        if (null != primaryKeyOfRecord)
        {
            auditRecordMap.put("FOREIGN_KEY", primaryKeyOfRecord);
        }
        if (null != primaryKeysString)
        {
            auditRecordMap.put("FKEY_CONDITION", primaryKeysString);
        }
    }
}



On Mon, Aug 10, 2015 at 7:27 AM, Aristedes Maniatis <ari@maniatis.org> wrote:
> On 10/08/2015 8:31pm, Hugi Thordarson wrote:
>> Is it possible for me to obtain the primary key for a Cayenne DataObject before committing
changes? I’m writing an audit log and I need the key for the object during PrePersist (where
I’m constructing the log object).
>
> How will it have a primary key before the record is written to the database? Or do you
want to hang onto the temporary ObjectId and then replace it with the real PK after the commit?
>
> Ari
>
>
> --
> -------------------------->
> Aristedes Maniatis
> GPG fingerprint CBFB 84B4 738D 4E87 5E5C  5EFA EF6A 7D2E 3E49 102A

Mime
View raw message