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