Thanks Mike! Although the approach I’m working on is a little different (and 
meant to be reusable with any Cayenne installation so can’t depend on 
superclass template modifications), it’s very helpful to see code from other 
Cayenne folks.

Cheers,
- hugi

// Hugi Thordarson
// http://www.loftfar.is/ <http://www.loftfar.is/>
// s. 895-6688



> On 10. ágú. 2015, at 12:53, Mike Kienenberger <mkien...@gmail.com> wrote:
> 
> 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 <a...@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

Reply via email to